def get_random_mass_point_particles(numPoints, massRangeParams): """ This function will generate a large set of points within the chosen mass and spin space. It will also return the corresponding PN spin coefficients for ease of use later (though these may be removed at some future point). Parameters ---------- numPoints : int Number of systems to simulate massRangeParams : massRangeParameters instance Instance holding all the details of mass ranges and spin ranges. Returns -------- mass : numpy.array List of the total masses. eta : numpy.array List of the symmetric mass ratios beta : numpy.array List of the 1.5PN beta spin coefficients sigma : numpy.array List of the 2PN sigma spin coefficients gamma : numpy.array List of the 2.5PN gamma spin coefficients spin1z : numpy.array List of the spin on the heavier body. NOTE: Body 1 is **always** the heavier body to remove mass,eta -> m1,m2 degeneracy spin2z : numpy.array List of the spin on the smaller body. NOTE: Body 2 is **always** the smaller body to remove mass,eta -> m1,m2 degeneracy mass1 : numpy.array List of the mass of the heavier body. NOTE: Body 1 is **always** the heavier body to remove mass,eta -> m1,m2 degeneracy mass2 : numpy.array List of the mass of the smaller body. NOTE: Body 2 is **always** the smaller body to remove mass,eta -> m1,m2 degeneracy """ # WARNING: We expect mass1 > mass2 ALWAYS # First we choose the total masses from a unifrom distribution in mass # to the -5/3. power. mass = numpy.random.random(numPoints) * \ (massRangeParams.minTotMass**(-5./3.) \ - massRangeParams.maxTotMass**(-5./3.)) \ + massRangeParams.maxTotMass**(-5./3.) mass = mass**(-3./5.) # Next we choose the mass ratios, this will take different limits based on # the value of total mass maxmass2 = numpy.minimum(mass/2., massRangeParams.maxMass2) minmass1 = numpy.maximum(massRangeParams.minMass1, mass/2.) mineta = numpy.maximum(massRangeParams.minCompMass \ * (mass-massRangeParams.minCompMass)/(mass*mass), \ massRangeParams.maxCompMass \ * (mass-massRangeParams.maxCompMass)/(mass*mass)) # Note that mineta is a numpy.array because mineta depends on the total # mass. Therefore this is not precomputed in the massRangeParams instance if massRangeParams.minEta: mineta = numpy.maximum(massRangeParams.minEta, mineta) # Eta also restricted by chirp mass restrictions if massRangeParams.min_chirp_mass: eta_val_at_min_chirp = massRangeParams.min_chirp_mass / mass eta_val_at_min_chirp = eta_val_at_min_chirp**(5./3.) mineta = numpy.maximum(mineta, eta_val_at_min_chirp) maxeta = numpy.minimum(massRangeParams.maxEta, maxmass2 \ * (mass - maxmass2) / (mass*mass)) maxeta = numpy.minimum(maxeta, minmass1 \ * (mass - minmass1) / (mass*mass)) # max eta also affected by chirp mass restrictions if massRangeParams.max_chirp_mass: eta_val_at_max_chirp = massRangeParams.max_chirp_mass / mass eta_val_at_max_chirp = eta_val_at_max_chirp**(5./3.) maxeta = numpy.minimum(maxeta, eta_val_at_max_chirp) if (maxeta < mineta).any(): errMsg = "ERROR: Maximum eta is smaller than minimum eta!!" raise ValueError(errMsg) eta = numpy.random.random(numPoints) * (maxeta - mineta) + mineta # Also calculate the component masses; mass1 > mass2 diff = (mass*mass * (1-4*eta))**0.5 mass1 = (mass + diff)/2. mass2 = (mass - diff)/2. # Check the masses are where we want them to be (allowing some floating # point rounding error). if (mass1 > massRangeParams.maxMass1*1.001).any() \ or (mass1 < massRangeParams.minMass1*0.999).any(): errMsg = "Mass1 is not within the specified mass range." raise ValueError(errMsg) if (mass2 > massRangeParams.maxMass2*1.001).any() \ or (mass2 < massRangeParams.minMass2*0.999).any(): errMsg = "Mass2 is not within the specified mass range." raise ValueError(errMsg) # Next up is the spins. First check if we have non-zero spins if massRangeParams.maxNSSpinMag == 0 and massRangeParams.maxBHSpinMag == 0: spinspin = numpy.zeros(numPoints,dtype=float) spin1z = numpy.zeros(numPoints,dtype=float) spin2z = numpy.zeros(numPoints,dtype=float) beta = numpy.zeros(numPoints,dtype=float) sigma = numpy.zeros(numPoints,dtype=float) gamma = numpy.zeros(numPoints,dtype=float) spin1z = numpy.zeros(numPoints,dtype=float) spin2z = numpy.zeros(numPoints,dtype=float) elif massRangeParams.nsbhFlag: # Spin 1 first mspin = numpy.zeros(len(mass1)) mspin += massRangeParams.maxBHSpinMag spin1z = (2*numpy.random.random(numPoints) - 1) * mspin # Then spin2 mspin = numpy.zeros(len(mass2)) mspin += massRangeParams.maxNSSpinMag spin2z = (2*numpy.random.random(numPoints) - 1) * mspin # And compute the PN components that come out of this beta, sigma, gamma, chiS = pnutils.get_beta_sigma_from_aligned_spins( eta, spin1z, spin2z) else: boundary_mass = massRangeParams.ns_bh_boundary_mass # Spin 1 first mspin = numpy.zeros(len(mass1)) mspin += massRangeParams.maxNSSpinMag mspin[mass1 > boundary_mass] = massRangeParams.maxBHSpinMag spin1z = (2*numpy.random.random(numPoints) - 1) * mspin # Then spin 2 mspin = numpy.zeros(len(mass2)) mspin += massRangeParams.maxNSSpinMag mspin[mass2 > boundary_mass] = massRangeParams.maxBHSpinMag spin2z = (2*numpy.random.random(numPoints) - 1) * mspin # And compute the PN components that come out of this beta, sigma, gamma, chiS = pnutils.get_beta_sigma_from_aligned_spins( eta, spin1z, spin2z) return mass,eta,beta,sigma,gamma,spin1z,spin2z,mass1,mass2
def get_random_mass_point_particles(numPoints, massRangeParams): """ This function will generate a large set of points within the chosen mass and spin space. It will also return the corresponding PN spin coefficients for ease of use later (though these may be removed at some future point). Parameters ---------- numPoints : int Number of systems to simulate massRangeParams : massRangeParameters instance Instance holding all the details of mass ranges and spin ranges. Returns -------- mass : numpy.array List of the total masses. eta : numpy.array List of the symmetric mass ratios beta : numpy.array List of the 1.5PN beta spin coefficients sigma : numpy.array List of the 2PN sigma spin coefficients gamma : numpy.array List of the 2.5PN gamma spin coefficients spin1z : numpy.array List of the spin on the heavier body. NOTE: Body 1 is **always** the heavier body to remove mass,eta -> m1,m2 degeneracy spin2z : numpy.array List of the spin on the smaller body. NOTE: Body 2 is **always** the smaller body to remove mass,eta -> m1,m2 degeneracy mass1 : numpy.array List of the mass of the heavier body. NOTE: Body 1 is **always** the heavier body to remove mass,eta -> m1,m2 degeneracy mass2 : numpy.array List of the mass of the smaller body. NOTE: Body 2 is **always** the smaller body to remove mass,eta -> m1,m2 degeneracy """ # WARNING: We expect mass1 > mass2 ALWAYS # First we choose the total masses from a unifrom distribution in mass # to the -5/3. power. mass = numpy.random.random(numPoints) * \ (massRangeParams.minTotMass**(-5./3.) \ - massRangeParams.maxTotMass**(-5./3.)) \ + massRangeParams.maxTotMass**(-5./3.) mass = mass**(-3. / 5.) # Next we choose the mass ratios, this will take different limits based on # the value of total mass maxmass2 = numpy.minimum(mass / 2., massRangeParams.maxMass2) minmass1 = numpy.maximum(massRangeParams.minMass1, mass / 2.) mineta = numpy.maximum(massRangeParams.minCompMass \ * (mass-massRangeParams.minCompMass)/(mass*mass), \ massRangeParams.maxCompMass \ * (mass-massRangeParams.maxCompMass)/(mass*mass)) # Note that mineta is a numpy.array because mineta depends on the total # mass. Therefore this is not precomputed in the massRangeParams instance if massRangeParams.minEta: mineta = numpy.maximum(massRangeParams.minEta, mineta) # Eta also restricted by chirp mass restrictions if massRangeParams.min_chirp_mass: eta_val_at_min_chirp = massRangeParams.min_chirp_mass / mass eta_val_at_min_chirp = eta_val_at_min_chirp**(5. / 3.) mineta = numpy.maximum(mineta, eta_val_at_min_chirp) maxeta = numpy.minimum(massRangeParams.maxEta, maxmass2 \ * (mass - maxmass2) / (mass*mass)) maxeta = numpy.minimum(maxeta, minmass1 \ * (mass - minmass1) / (mass*mass)) # max eta also affected by chirp mass restrictions if massRangeParams.max_chirp_mass: eta_val_at_max_chirp = massRangeParams.max_chirp_mass / mass eta_val_at_max_chirp = eta_val_at_max_chirp**(5. / 3.) maxeta = numpy.minimum(maxeta, eta_val_at_max_chirp) if (maxeta < mineta).any(): errMsg = "ERROR: Maximum eta is smaller than minimum eta!!" raise ValueError(errMsg) eta = numpy.random.random(numPoints) * (maxeta - mineta) + mineta # Also calculate the component masses; mass1 > mass2 diff = (mass * mass * (1 - 4 * eta))**0.5 mass1 = (mass + diff) / 2. mass2 = (mass - diff) / 2. # Check the masses are where we want them to be (allowing some floating # point rounding error). if (mass1 > massRangeParams.maxMass1*1.001).any() \ or (mass1 < massRangeParams.minMass1*0.999).any(): errMsg = "Mass1 is not within the specified mass range." raise ValueError(errMsg) if (mass2 > massRangeParams.maxMass2*1.001).any() \ or (mass2 < massRangeParams.minMass2*0.999).any(): errMsg = "Mass2 is not within the specified mass range." raise ValueError(errMsg) # Next up is the spins. First check if we have non-zero spins if massRangeParams.maxNSSpinMag == 0 and massRangeParams.maxBHSpinMag == 0: spinspin = numpy.zeros(numPoints, dtype=float) spin1z = numpy.zeros(numPoints, dtype=float) spin2z = numpy.zeros(numPoints, dtype=float) beta = numpy.zeros(numPoints, dtype=float) sigma = numpy.zeros(numPoints, dtype=float) gamma = numpy.zeros(numPoints, dtype=float) spin1z = numpy.zeros(numPoints, dtype=float) spin2z = numpy.zeros(numPoints, dtype=float) elif massRangeParams.nsbhFlag: # Spin 1 first mspin = numpy.zeros(len(mass1)) mspin += massRangeParams.maxBHSpinMag spin1z = (2 * numpy.random.random(numPoints) - 1) * mspin # Then spin2 mspin = numpy.zeros(len(mass2)) mspin += massRangeParams.maxNSSpinMag spin2z = (2 * numpy.random.random(numPoints) - 1) * mspin # And compute the PN components that come out of this beta, sigma, gamma, chiS = pnutils.get_beta_sigma_from_aligned_spins( eta, spin1z, spin2z) else: boundary_mass = massRangeParams.ns_bh_boundary_mass # Spin 1 first mspin = numpy.zeros(len(mass1)) mspin += massRangeParams.maxNSSpinMag mspin[mass1 > boundary_mass] = massRangeParams.maxBHSpinMag spin1z = (2 * numpy.random.random(numPoints) - 1) * mspin # Then spin 2 mspin = numpy.zeros(len(mass2)) mspin += massRangeParams.maxNSSpinMag mspin[mass2 > boundary_mass] = massRangeParams.maxBHSpinMag spin2z = (2 * numpy.random.random(numPoints) - 1) * mspin # And compute the PN components that come out of this beta, sigma, gamma, chiS = pnutils.get_beta_sigma_from_aligned_spins( eta, spin1z, spin2z) return mass, eta, beta, sigma, gamma, spin1z, spin2z, mass1, mass2
def get_point_distance(point1, point2, metricParams, fUpper): """ Function to calculate the mismatch between two points, supplied in terms of the masses and spins, using the xi_i parameter space metric to approximate the mismatch of the two points. Can also take one of the points as an array of points and return an array of mismatches (but only one can be an array!) point1 : List of floats or numpy.arrays point1[0] contains the mass(es) of the heaviest body(ies). point1[1] contains the mass(es) of the smallest body(ies). point1[2] contains the spin(es) of the heaviest body(ies). point1[3] contains the spin(es) of the smallest body(ies). point2 : List of floats point2[0] contains the mass of the heaviest body. point2[1] contains the mass of the smallest body. point2[2] contains the spin of the heaviest body. point2[3] contains the spin of the smallest body. 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. fUpper : float The value of fUpper to use when getting the mu coordinates from the lambda coordinates. This must be a key in metricParams.evals, metricParams.evecs and metricParams.evecsCV (ie. we must know how to do the transformation for the given value of fUpper) Returns -------- dist : float or numpy.array Distance between the point2 and all points in point1 xis1 : List of floats or numpy.arrays Position of the input point1(s) in the xi_i parameter space xis2 : List of floats Position of the input point2 in the xi_i parameter space """ aMass1 = point1[0] aMass2 = point1[1] aSpin1 = point1[2] aSpin2 = point1[3] try: leng = len(aMass1) aArray = True except: aArray = False bMass1 = point2[0] bMass2 = point2[1] bSpin1 = point2[2] bSpin2 = point2[3] bArray = False aTotMass = aMass1 + aMass2 aEta = (aMass1 * aMass2) / (aTotMass * aTotMass) aCM = aTotMass * aEta**(3./5.) bTotMass = bMass1 + bMass2 bEta = (bMass1 * bMass2) / (bTotMass * bTotMass) bCM = bTotMass * bEta**(3./5.) abeta, asigma, agamma, achis = pnutils.get_beta_sigma_from_aligned_spins( aEta, aSpin1, aSpin2) bbeta, bsigma, bgamma, bchis = pnutils.get_beta_sigma_from_aligned_spins( bEta, bSpin1, bSpin2) aXis = get_cov_params(aTotMass, aEta, abeta, asigma, agamma, achis, \ metricParams, fUpper) bXis = get_cov_params(bTotMass, bEta, bbeta, bsigma, bgamma, bchis, \ metricParams, fUpper) dist = (aXis[0] - bXis[0])**2 for i in xrange(1,len(aXis)): dist += (aXis[i] - bXis[i])**2 return dist, aXis, bXis
def get_point_distance(point1, point2, metricParams, fUpper): """ Function to calculate the mismatch between two points, supplied in terms of the masses and spins, using the xi_i parameter space metric to approximate the mismatch of the two points. Can also take one of the points as an array of points and return an array of mismatches (but only one can be an array!) point1 : List of floats or numpy.arrays point1[0] contains the mass(es) of the heaviest body(ies). point1[1] contains the mass(es) of the smallest body(ies). point1[2] contains the spin(es) of the heaviest body(ies). point1[3] contains the spin(es) of the smallest body(ies). point2 : List of floats point2[0] contains the mass of the heaviest body. point2[1] contains the mass of the smallest body. point2[2] contains the spin of the heaviest body. point2[3] contains the spin of the smallest body. 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. fUpper : float The value of fUpper to use when getting the mu coordinates from the lambda coordinates. This must be a key in metricParams.evals, metricParams.evecs and metricParams.evecsCV (ie. we must know how to do the transformation for the given value of fUpper) Returns -------- dist : float or numpy.array Distance between the point2 and all points in point1 xis1 : List of floats or numpy.arrays Position of the input point1(s) in the xi_i parameter space xis2 : List of floats Position of the input point2 in the xi_i parameter space """ aMass1 = point1[0] aMass2 = point1[1] aSpin1 = point1[2] aSpin2 = point1[3] try: leng = len(aMass1) aArray = True except: aArray = False bMass1 = point2[0] bMass2 = point2[1] bSpin1 = point2[2] bSpin2 = point2[3] bArray = False aTotMass = aMass1 + aMass2 aEta = (aMass1 * aMass2) / (aTotMass * aTotMass) aCM = aTotMass * aEta**(3. / 5.) bTotMass = bMass1 + bMass2 bEta = (bMass1 * bMass2) / (bTotMass * bTotMass) bCM = bTotMass * bEta**(3. / 5.) abeta, asigma, agamma, achis = pnutils.get_beta_sigma_from_aligned_spins( aEta, aSpin1, aSpin2) bbeta, bsigma, bgamma, bchis = pnutils.get_beta_sigma_from_aligned_spins( bEta, bSpin1, bSpin2) aXis = get_cov_params(aTotMass, aEta, abeta, asigma, agamma, achis, \ metricParams, fUpper) bXis = get_cov_params(bTotMass, bEta, bbeta, bsigma, bgamma, bchis, \ metricParams, fUpper) dist = (aXis[0] - bXis[0])**2 for i in xrange(1, len(aXis)): dist += (aXis[i] - bXis[i])**2 return dist, aXis, bXis
def get_mass_distribution(bestMasses, scaleFactor, massRangeParams, metricParams, fUpper, numJumpPoints=100, chirpMassJumpFac=0.0001, etaJumpFac=0.01, spin1zJumpFac=0.01, spin2zJumpFac=0.01): """ Given a set of masses, this function will create a set of points nearby in the mass space and map these to the xi space. Parameters ----------- bestMasses : list Contains [ChirpMass, eta, spin1z, spin2z]. Points will be placed around tjos scaleFactor : float This parameter describes the radius away from bestMasses that points will be placed in. massRangeParams : massRangeParameters instance Instance holding all the details of mass ranges and spin ranges. 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. fUpper : float The value of fUpper that was used when obtaining the xi_i coordinates. This lets us know how to rotate potential physical points into the correct xi_i space. This must be a key in metricParams.evals, metricParams.evecs and metricParams.evecsCV (ie. we must know how to do the transformation for the given value of fUpper) numJumpPoints : int, optional (default = 100) The number of points that will be generated every iteration chirpMassJumpFac : float, optional (default=0.0001) The jump points will be chosen with fractional variation in chirpMass up to this multiplied by scaleFactor. etaJumpFac : float, optional (default=0.01) The jump points will be chosen with fractional variation in eta up to this multiplied by scaleFactor. spin1zJumpFac : float, optional (default=0.01) The jump points will be chosen with absolute variation in spin1z up to this multiplied by scaleFactor. spin2zJumpFac : float, optional (default=0.01) The jump points will be chosen with absolute variation in spin2z up to this multiplied by scaleFactor. Returns -------- Chirpmass : numpy.array chirp mass of the resulting points Totmass : numpy.array Total mass of the resulting points Eta : numpy.array Symmetric mass ratio of the resulting points Spin1z : numpy.array Spin of the heavier body of the resulting points Spin2z : numpy.array Spin of the smaller body of the resulting points Diff : numpy.array Mass1 - Mass2 of the resulting points Mass1 : numpy.array Mass1 (mass of heavier body) of the resulting points Mass2 : numpy.array Mass2 (mass of smaller body) of the resulting points Beta : numpy.array 1.5PN spin phasing coefficient of the resulting points Sigma : numpy.array 2PN spin phasing coefficient of the resulting points Gamma : numpy.array 2.5PN spin phasing coefficient of the resulting points Chis : numpy.array 0.5 * (spin1z + spin2z) for the resulting points new_xis : list of numpy.array Position of points in the xi coordinates """ # FIXME: It would be better if rejected values could be drawn from the # full possible mass/spin distribution. However speed in this function is # a major factor and must be considered. bestChirpmass = bestMasses[0] bestEta = bestMasses[1] bestSpin1z = bestMasses[2] bestSpin2z = bestMasses[3] # Firstly choose a set of values for masses and spins chirpmass = bestChirpmass * (1 - (numpy.random.random(numJumpPoints)-0.5) \ * chirpMassJumpFac * scaleFactor ) etaRange = massRangeParams.maxEta - massRangeParams.minEta currJumpFac = etaJumpFac * scaleFactor if currJumpFac > etaRange: currJumpFac = etaRange eta = bestEta * ( 1 - (numpy.random.random(numJumpPoints) - 0.5) \ * currJumpFac) maxSpinMag = max(massRangeParams.maxNSSpinMag, massRangeParams.maxBHSpinMag) minSpinMag = min(massRangeParams.maxNSSpinMag, massRangeParams.maxBHSpinMag) # Note that these two are cranged by spinxzFac, *not* spinxzFac/spinxz currJumpFac = spin1zJumpFac * scaleFactor if currJumpFac > maxSpinMag: currJumpFac = maxSpinMag # Actually set the new spin trial points if massRangeParams.nsbhFlag or (maxSpinMag == minSpinMag): curr_spin_1z_jump_fac = currJumpFac curr_spin_2z_jump_fac = currJumpFac # Check spins aren't going to be unphysical if currJumpFac > massRangeParams.maxBHSpinMag: curr_spin_1z_jump_fac = massRangeParams.maxBHSpinMag if currJumpFac > massRangeParams.maxNSSpinMag: curr_spin_2z_jump_fac = massRangeParams.maxNSSpinMag spin1z = bestSpin1z + ( (numpy.random.random(numJumpPoints) - 0.5) \ * curr_spin_1z_jump_fac) spin2z = bestSpin2z + ( (numpy.random.random(numJumpPoints) - 0.5) \ * curr_spin_2z_jump_fac) else: # If maxNSSpinMag is very low (0) and maxBHSpinMag is high we can # find it hard to place any points. So mix these when # masses are swapping between the NS and BH. curr_spin_bh_jump_fac = currJumpFac curr_spin_ns_jump_fac = currJumpFac # Check spins aren't going to be unphysical if currJumpFac > massRangeParams.maxBHSpinMag: curr_spin_bh_jump_fac = massRangeParams.maxBHSpinMag if currJumpFac > massRangeParams.maxNSSpinMag: curr_spin_ns_jump_fac = massRangeParams.maxNSSpinMag spin1z = numpy.zeros(numJumpPoints, dtype=float) spin2z = numpy.zeros(numJumpPoints, dtype=float) split_point = int(numJumpPoints/2) # So set the first half to be at least within the BH range and the # second half to be at least within the NS range spin1z[:split_point] = bestSpin1z + \ ( (numpy.random.random(split_point) - 0.5)\ * curr_spin_bh_jump_fac) spin1z[split_point:] = bestSpin1z + \ ( (numpy.random.random(numJumpPoints-split_point) - 0.5)\ * curr_spin_ns_jump_fac) spin2z[:split_point] = bestSpin2z + \ ( (numpy.random.random(split_point) - 0.5)\ * curr_spin_bh_jump_fac) spin2z[split_point:] = bestSpin2z + \ ( (numpy.random.random(numJumpPoints-split_point) - 0.5)\ * curr_spin_ns_jump_fac) # Point[0] is always set to the original point chirpmass[0] = bestChirpmass eta[0] = bestEta spin1z[0] = bestSpin1z spin2z[0] = bestSpin2z # Remove points where eta becomes unphysical eta[eta > massRangeParams.maxEta] = massRangeParams.maxEta if massRangeParams.minEta: eta[eta < massRangeParams.minEta] = massRangeParams.minEta else: eta[eta < 0.0001] = 0.0001 # Total mass, masses and mass diff totmass = chirpmass / (eta**(3./5.)) diff = (totmass*totmass * (1-4*eta))**0.5 mass1 = (totmass + diff)/2. mass2 = (totmass - diff)/2. # Check the validity of the spin values # Do the first spin if maxSpinMag == 0: # Shortcut if non-spinning pass elif massRangeParams.nsbhFlag or (maxSpinMag == minSpinMag): # Simple case where I don't have to worry about correlation with mass numploga = abs(spin1z) > massRangeParams.maxBHSpinMag spin1z[numploga] = 0 else: # Do have to consider masses boundary_mass = massRangeParams.ns_bh_boundary_mass numploga1 = numpy.logical_and(mass1 >= boundary_mass, abs(spin1z) <= massRangeParams.maxBHSpinMag) numploga2 = numpy.logical_and(mass1 < boundary_mass, abs(spin1z) <= massRangeParams.maxNSSpinMag) numploga = numpy.logical_or(numploga1, numploga2) numploga = numpy.logical_not(numploga) spin1z[numploga] = 0 # Same for the second spin if maxSpinMag == 0: # Shortcut if non-spinning pass elif massRangeParams.nsbhFlag or (maxSpinMag == minSpinMag): numplogb = abs(spin2z) > massRangeParams.maxNSSpinMag spin2z[numplogb] = 0 else: # Do have to consider masses boundary_mass = massRangeParams.ns_bh_boundary_mass numplogb1 = numpy.logical_and(mass2 >= boundary_mass, abs(spin2z) <= massRangeParams.maxBHSpinMag) numplogb2 = numpy.logical_and(mass2 < boundary_mass, abs(spin2z) <= massRangeParams.maxNSSpinMag) numplogb = numpy.logical_or(numplogb1, numplogb2) numplogb = numpy.logical_not(numplogb) spin2z[numplogb] = 0 # Get the various spin-derived quantities beta, sigma, gamma, chis = get_beta_sigma_from_aligned_spins(eta, spin1z, spin2z) if (maxSpinMag) and (numploga[0] or numplogb[0]): raise ValueError("Cannot remove the guide point!") # And remove points where the individual masses are outside of the physical # range. Or the total masses are. # These "removed" points will have metric distances that will be much, much # larger than any thresholds used in the functions in brute_force_utils.py # and will always be rejected. An unphysical value cannot be used as it # would result in unphysical metric distances and cause failures. totmass[mass1 < massRangeParams.minMass1] = 0.0001 totmass[mass1 > massRangeParams.maxMass1] = 0.0001 totmass[mass2 < massRangeParams.minMass2] = 0.0001 totmass[mass2 > massRangeParams.maxMass2] = 0.0001 # There is some numerical error which can push this a bit higher. We do # *not* want to reject the initial guide point. This error comes from # Masses -> totmass, eta -> masses conversion, we will have points pushing # onto the boudaries of the space. totmass[totmass > massRangeParams.maxTotMass*1.0001] = 0.0001 totmass[totmass < massRangeParams.minTotMass*0.9999] = 0.0001 if massRangeParams.max_chirp_mass: totmass[chirpmass > massRangeParams.max_chirp_mass*1.0001] = 0.0001 if massRangeParams.min_chirp_mass: totmass[chirpmass < massRangeParams.min_chirp_mass*0.9999] = 0.0001 if totmass[0] < 0.00011: raise ValueError("Cannot remove the guide point!") # Then map to xis new_xis = get_cov_params(totmass, eta, beta, sigma, gamma, chis, metricParams, fUpper) return chirpmass, totmass, eta, spin1z, spin2z, diff, mass1, mass2, beta, \ sigma, gamma, chis, new_xis
def add_point_by_masses(self, mass1, mass2, spin1z, spin2z, vary_fupper=False): """ Add a point to the template bank. This differs from add point to bank as it assumes that the chi coordinates and the products needed to use vary_fupper have not already been calculated. This function calculates these products and then calls add_point_by_chi_coords. This function also carries out a number of sanity checks (eg. is the point within the ranges given by mass_range_params) that add_point_by_chi_coords does not do for speed concerns. Parameters ----------- mass1 : float Mass of the heavier body mass2 : float Mass of the lighter body spin1z : float Spin of the heavier body spin2z : float Spin of the lighter body """ # Test that masses are the expected way around (ie. mass1 > mass2) if mass2 > mass1: if not self.spin_warning_given: warn_msg = "Am adding a template where mass2 > mass1. The " warn_msg += "convention is that mass1 > mass2. Swapping mass1 " warn_msg += "and mass2 and adding point to bank. This message " warn_msg += "will not be repeated." logging.warn(warn_msg) self.spin_warning_given = True # These that masses obey the restrictions of mass_range_params if self.mass_range_params.is_outside_range(mass1, mass2, spin1z, spin2z): err_msg = "Point with masses given by " err_msg += "%f %f %f %f " % (mass1, mass2, spin1z, spin2z) err_msg += "(mass1, mass2, spin1z, spin2z) is not consistent " err_msg += "with the provided command-line restrictions on masses " err_msg += "and spins." raise ValueError(err_msg) # Get beta, sigma, gamma tot_mass = mass1 + mass2 eta = mass1 * mass2 / (tot_mass * tot_mass) beta, sigma, gamma, chis = pnutils.get_beta_sigma_from_aligned_spins(\ eta, spin1z, spin2z) # Get chi coordinates chi_coords = coord_utils.get_cov_params(tot_mass, eta, beta, sigma, gamma, chis, self.metric_params, self.ref_freq) # Get mus and best fupper for this point, if needed if vary_fupper: mass_dict = {} mass_dict['m1'] = numpy.array([mass1]) mass_dict['m2'] = numpy.array([mass2]) mass_dict['s1z'] = numpy.array([spin1z]) mass_dict['s2z'] = numpy.array([spin2z]) freqs = numpy.array([self.frequency_map.keys()], dtype=float) freq_cutoff = coord_utils.return_nearest_cutoff(\ self.upper_freq_formula, mass_dict, freqs) freq_cutoff = freq_cutoff[0] lambdas = coord_utils.get_chirp_params(tot_mass, eta, beta, sigma, gamma, chis, self.metric_params.f0, self.metric_params.pnOrder) mus = [] for freq in self.frequency_map: mus.append( coord_utils.get_mu_params(lambdas, self.metric_params, freq)) mus = numpy.array(mus) else: freq_cutoff = None mus = None self.add_point_by_chi_coords(chi_coords, mass1, mass2, spin1z, spin2z, point_fupper=freq_cutoff, mus=mus)
def add_point_by_masses(self, mass1, mass2, spin1z, spin2z, vary_fupper=False): """ Add a point to the template bank. This differs from add point to bank as it assumes that the chi coordinates and the products needed to use vary_fupper have not already been calculated. This function calculates these products and then calls add_point_by_chi_coords. This function also carries out a number of sanity checks (eg. is the point within the ranges given by mass_range_params) that add_point_by_chi_coords does not do for speed concerns. Parameters ----------- mass1 : float Mass of the heavier body mass2 : float Mass of the lighter body spin1z : float Spin of the heavier body spin2z : float Spin of the lighter body """ # Test that masses are the expected way around (ie. mass1 > mass2) if mass2 > mass1: if not self.spin_warning_given: warn_msg = "Am adding a template where mass2 > mass1. The " warn_msg += "convention is that mass1 > mass2. Swapping mass1 " warn_msg += "and mass2 and adding point to bank. This message " warn_msg += "will not be repeated." logging.warn(warn_msg) self.spin_warning_given = True # These that masses obey the restrictions of mass_range_params if self.mass_range_params.is_outside_range(mass1, mass2, spin1z, spin2z): err_msg = "Point with masses given by " err_msg += "%f %f %f %f " %(mass1, mass2, spin1z, spin2z) err_msg += "(mass1, mass2, spin1z, spin2z) is not consistent " err_msg += "with the provided command-line restrictions on masses " err_msg += "and spins." raise ValueError(err_msg) # Get beta, sigma, gamma tot_mass = mass1 + mass2 eta = mass1 * mass2 / (tot_mass * tot_mass) beta, sigma, gamma, chis = pnutils.get_beta_sigma_from_aligned_spins(\ eta, spin1z, spin2z) # Get chi coordinates chi_coords = coord_utils.get_cov_params(tot_mass, eta, beta, sigma, gamma, chis, self.metric_params, self.ref_freq) # Get mus and best fupper for this point, if needed if vary_fupper: freq_cutoff = coord_utils.return_nearest_cutoff(\ self.upper_freq_formula, tot_mass, self.frequency_map) lambdas = coord_utils.get_chirp_params(tot_mass, eta, beta, sigma, gamma, chis, self.metric_params.f0, self.metric_params.pnOrder) mus = [] for freq in self.frequency_map: mus.append(coord_utils.get_mu_params(lambdas, self.metric_params, freq) ) mus = numpy.array(mus) else: freq_cutoff=None mus=None self.add_point_by_chi_coords(chi_coords, mass1, mass2, spin1z, spin2z, point_fupper=freq_cutoff, mus=mus)
def get_chirp_params_old(mass1, mass2, spin1z, spin2z, f0, order): """ Take a set of masses and spins and convert to the various lambda coordinates that describe the orbital phase. Accepted PN orders are: {} Parameters ---------- mass1 : float or array Mass1 of input(s). mass2 : float or array Mass2 of input(s). spin1z : float or array Parallel spin component(s) of body 1. spin2z : float or array Parallel spin component(s) of body 2. f0 : float This is an arbitrary scaling factor introduced to avoid the potential for numerical overflow when calculating this. Generally the default value (70) is safe here. **IMPORTANT, if you want to calculate the ethinca metric components later this MUST be set equal to f_low.** This value must also be used consistently (ie. don't change its value when calling different functions!). order : string The Post-Newtonian order that is used to translate from masses and spins to the lambda_i coordinate system. Valid orders given above. Returns -------- lambdas : list of floats or numpy.arrays The lambda coordinates for the input system(s) """ totmass, eta = pnutils.mass1_mass2_to_mtotal_eta(mass1, mass2) beta, sigma, gamma = pnutils.get_beta_sigma_from_aligned_spins(\ eta, spin1z, spin2z) # Convert mass to seconds totmass = totmass * MTSUN_SI pi = numpy.pi mapping = generate_inverse_mapping(order) lambdas = [] for idx in xrange(len(mapping.keys())): if mapping[idx] == 'Lambda0': lambda0 = 3. / (128. * eta * (pi * totmass * f0)**(5./3.)) lambdas.append(lambda0) elif mapping[idx] == 'Lambda2': lambda2 = 5. / (96. * pi * eta * totmass * f0) \ * (743./336. + 11./4. * eta) lambdas.append(lambda2) elif mapping[idx] == 'Lambda3': lambda3 = (-3. * pi**(1./3.))/(8. * eta * (totmass*f0)**(2./3.)) \ * (1. - beta/ (4. * pi)) lambdas.append(lambda3) elif mapping[idx] == 'Lambda4': lambda4 = 15. / (64. * eta * (pi * totmass * f0)**(1./3.)) * \ (3058673./1016064. + 5429./1008. * eta + 617./144. * \ eta**2 - sigma) lambdas.append(lambda4) # No Lambda5 term is present as that would be equivalent to a constant # phase offset, and thus completely degenerate with the initial orbital # phase. elif mapping[idx] == 'LogLambda5': loglambda5 = 3. * (38645.*pi/756. - 65.*pi*eta/9. - gamma) loglambda5 = loglambda5 * (3./(128.*eta)) lambdas.append(loglambda5) elif mapping[idx] == 'Lambda6': lambda6 = 11583231236531./4694215680. - (640.*pi*pi)/3.\ - (6848.*GAMMA)/21. lambda6 -= (6848./21.) * numpy.log(4 * (pi*totmass*f0)**(1./3.)) lambda6 += (-15737765635/3048192. + 2255.*pi*pi/12.)*eta lambda6 += (76055.*eta*eta)/1728. - (127825.*eta*eta*eta)/1296. lambda6 = lambda6 * 3./(128.*eta) * (pi * totmass * f0)**(1/3.) lambdas.append(lambda6) elif mapping[idx] == 'LogLambda6': loglambda6 = -( 6848./21) loglambda6 = loglambda6 * 3./(128.*eta)\ * (pi * totmass * f0)**(1/3.) lambdas.append(loglambda6) elif mapping[idx] == 'Lambda7': lambda7 = (77096675.*pi)/254016. + (378515.*pi*eta)/1512. \ - (74045.*pi*eta*eta)/756. lambda7 = lambda7 * 3./(128.*eta) * (pi * totmass * f0)**(2/3.) lambdas.append(lambda7) else: err_msg = "Do not understand term {}.".format(mapping[idx]) raise ValueError(err_msg) return lambdas