Skip to content

Commit

Permalink
Merge pull request #92 from nanograv/dev-ecg-merge
Browse files Browse the repository at this point in the history
New Realizer_SAM in extensions module
  • Loading branch information
emikocgardiner authored Nov 4, 2023
2 parents 7d4de9f + 9481afa commit 533932e
Show file tree
Hide file tree
Showing 7 changed files with 1,670 additions and 315 deletions.
2 changes: 1 addition & 1 deletion holodeck/anisotropy.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def healpix_map(hc_ss, hc_bg, nside=NSIDE, seed=None, ret_seed=False):
np.random.seed(seed)

# spread background evenly across pixels in moll_hc
moll_hc = np.ones((nfreqs,nreals,npix)) * hc_bg[:,:,np.newaxis]**2/(npix/area) # (frequency, realization, pixel)
moll_hc = np.ones((nfreqs,nreals,npix)) * hc_bg[:,:,np.newaxis]**2/(npix*area) # (frequency, realization, pixel)

# choose random pixels to place the single sources
pix_ss = np.random.randint(0, npix-1, size=nfreqs*nreals*nloudest).reshape(nfreqs, nreals, nloudest)
Expand Down
285 changes: 283 additions & 2 deletions holodeck/cyutils.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cimport scipy.special.cython_special as sp_special
from libc.stdio cimport printf
from libc.stdlib cimport malloc, free, qsort
# make sure to use c-native math functions instead of python/numpy
from libc.math cimport pow, sqrt, abs, M_PI, NAN
from libc.math cimport pow, sqrt, abs, M_PI, NAN, cos, sin

from cpython.pycapsule cimport PyCapsule_IsValid, PyCapsule_GetPointer
from numpy.random cimport bitgen_t
Expand Down Expand Up @@ -1815,4 +1815,285 @@ cdef double[:, :] _interp_2d(
else:
ynew[ii, nn] = NAN

return ynew
return ynew








# ==================================================================================================
# ==== DetStats Functions ====
# ==================================================================================================


def gamma_of_rho_interp(rho, rsort, rho_interp_grid, gamma_interp_grid):
"""
rho : 1Darray of scalars
SNR of single sources, in flat array
rsort : 1Darray
order of flat rho values smallest to largest
rho_interp_grid : 1Darray
rho values corresponding to each gamma
gamma_interp_grid : 1Darray
gamma values corresponding to each rho
"""
# pass in the interp grid
cdef np.ndarray[np.double_t, ndim=1] gamma = np.zeros(rho.shape)

_gamma_of_rho_interp(rho, rsort, rho_interp_grid, gamma_interp_grid, gamma)

return gamma

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
cdef int _gamma_of_rho_interp(
double[:] rho, long[:] rsort,
double[:] rho_interp_grid, double[:] gamma_interp_grid,
# output
double[:] gamma
):
""" Find gamma of rho by interpolation over rho and gamma grids.
"""

cdef int n_rho = rho.size
cdef int n_interp = rho_interp_grid.size
cdef int ii, kk, rr
ii = 0 # get rho in order using rho[rsort[ii]]

for kk in range(n_rho):
rr = rsort[kk] # index of next largest rho, equiv to rev in redz calculation
# print('kk =',kk,' rr =', rr, 'rho[rr] =', rho[rr])
# get to the right index of the interpolation-grid
while (rho_interp_grid[ii+1] < rho[rr]) and (ii < n_interp -2):
ii += 1
# print('ii =',ii, ' rho_interp[ii] =', rho_interp_grid[ii], ' rho_interp[ii+1] =', rho_interp_grid[ii+1])
# interpolate
gamma[rr] = interp_at_index(ii, rho[rr], rho_interp_grid, gamma_interp_grid)
# print('rho =', rho[rr], ' gamma =', gamma[rr], '\n')\

return 0


def snr_ss(amp, F_iplus, F_icross, iotas, dur, Phi_0, S_i, freqs):
""" Calculate single source SNR
Parameters
----------
amp : (F,R,L) NDarray
Dimensionless strain amplitude of loudest single sources
F_iplus : (P,F,S,L) NDarray
Antenna pattern function for each pulsar.
F_icross : (P,F,S,L) NDarray
Antenna pattern function for each pulsar.
iotas : (F,S,L) NDarray
Inclination, used to calculate:
a_pol = 1 + np.cos(iotas) **2
b_pol = -2 * np.cos(iotas)
dur : scalar
Duration of observations.
Phi_0 : (F,S,L) NDarray
Initial GW phase
S_i : (P,F,R,L) NDarray
Total noise of each pulsar wrt detection of each single source, in s^3
freqs : (F,) 1Darray
Observed frequency bin centers.
Returns
-------
snr_ss : (F,R,S,L) NDarray
SNR from the whole PTA for each single source with
each realized sky position (S) and realized strain (R)
"""
nfreqs, nreals, nloudest = amp.shape[0], amp.shape[1], amp.shape[2]
npsrs, nskies = F_iplus.shape[0], F_iplus.shape[2]
cdef np.ndarray[np.double_t, ndim=4] snr_ss = np.zeros((nfreqs, nreals, nskies, nloudest))
_snr_ss(
amp, F_iplus, F_icross, iotas, dur, Phi_0, S_i, freqs,
npsrs, nfreqs, nreals, nskies, nloudest,
snr_ss)
return snr_ss

@cython.boundscheck(False)
@cython.wraparound(False)
@cython.nonecheck(False)
@cython.cdivision(True)
cdef int _snr_ss(
double[:,:,:] amp,
double[:,:,:,:] F_iplus,
double[:,:,:,:] F_icross,
double[:,:,:] iotas,
double dur,
double[:,:,:] Phi_0,
double[:,:,:,:] S_i,
double[:] freqs,
long npsrs, long nfreqs, long nreals, long nskies, long nloudest,
# output
double[:,:,:,:] snr_ss
):
"""
Parameters
----------
amp : (F,R,L) NDarray
Dimensionless strain amplitude of loudest single sources
F_iplus : (P,F,S,L) NDarray
Antenna pattern function for each pulsar.
F_icross : (P,F,S,L) NDarray
Antenna pattern function for each pulsar.
iotas : (F,S,L) NDarray
Inclination, used to calculate:
a_pol = 1 + np.cos(iotas) **2
b_pol = -2 * np.cos(iotas)
dur : scalar
Duration of observations.
Phi_0 : (F,S,L) NDarray
Initial GW phase
S_i : (P,F,R,L) NDarray
Total noise of each pulsar wrt detection of each single source, in s^3
freqs : (F,) 1Darray
Observed frequency bin centers.
snr_ss : (F,R,S,L) NDarray
Pointer to single source SNR array, to be calculated.
NOTE: This may be improved by moving some of the math outside the function.
I.e., passing in sin/cos of NDarrays to be used.
"""

cdef int pp, ff, rr, ss, ll
cdef float a_pol, b_pol, Phi_T, pta_snr_sq, coef, term1, term2, term3
# print('npsrs %d, nfreqs %d, nreals %d, nskies %d, nloudest %d' % (npsrs, nfreqs, nreals, nskies, nloudest))

for ff in range(nfreqs):
for ss in range(nskies):
for ll in range(nloudest):
a_pol = 1 + pow(cos(iotas[ff,ss,ll]), 2.0)
b_pol = -2 * cos(iotas[ff,ss,ll])
Phi_T = 2 * M_PI * freqs[ff] * dur + Phi_0[ff,ss,ll]
for rr in range(nreals):
pta_snr_sq = 0
for pp in range(npsrs):
# calculate coefficient depending on
# function of amp, S_i, and freqs
coef = pow(amp[ff,rr,ll], 2.0) / (S_i[pp,ff,rr,ll] * 8 * pow(M_PI * freqs[ff], 3.0))

# calculate terms that depend on p, f, s, and l
# functions of F_iplus, F_icross, a_pol, b_pol, Phi_0, and Phi_T
term1 = (
pow(a_pol * F_iplus[pp,ff,ss,ll], 2.0)
* (Phi_T * (1.0 + 2.0 * pow(sin(Phi_0[ff,ss,ll]), 2.0))
+ cos(Phi_T) * (-1.0 * sin(Phi_T) + 4.0 * cos(Phi_0[ff,ss,ll]))
- 4.0 * sin(Phi_0[ff,ss,ll])
)
)
term2 = (
pow(b_pol * F_icross[pp,ff,ss,ll], 2.0)
* (Phi_T * (1.0 + 2.0 * pow(cos(Phi_0[ff,ss,ll]), 2.0))
+ sin(Phi_T) * cos(Phi_T) - 4.0 * cos(Phi_0[ff,ss,ll])
)
)
term3 = (
-2.0 * a_pol * b_pol * F_iplus[pp,ff,ss,ll] * F_icross[pp,ff,ss,ll]
* (2.0 * Phi_T * sin(Phi_T) *cos(Phi_0[ff,ss,ll])
+ sin(Phi_T) * (sin(Phi_T) - 2.0 * sin(Phi_0[ff,ss,ll])
+ 2.0 * cos(Phi_T) * cos(Phi_0[ff,ss,ll])
- 2.0 * cos(Phi_0[ff,ss,ll])
)
)
)
pta_snr_sq += coef*(term1 + term2 + term3) # sum snr^2 of all pulsars for a single source

# set snr for a single source, using sum from all pulsars
snr_ss[ff,rr,ss,ll] = sqrt(pta_snr_sq)


def Sh_rest(hc_ss, hc_bg, freqs, nexcl):
"""
Calculate the noise from all the single sources except the source in question
and the next N_excl loudest sources.
Parameters
----------
hc_ss : (F,R,L) NDarray
Characteristic strain from all loud single sources.
hc_bg : (F,R) NDarray
Characteristic strain from all but loudest source at each frequency.
freqs : (F,) 1Darray
Frequency bin centers.
nexcl : int
Number of loudest single sources to exclude from hc_rest noise, in addition
to the source in question.
Returns
-------
Sh_rest : (F,R,L) NDarray of scalars
The noise in a single pulsar from other GW sources for detecting each single source.
"""

nfreqs, nreals, nloudest = hc_ss.shape[0], hc_ss.shape[1], hc_ss.shape[2]
cdef np.ndarray[np.double_t, ndim=3] Sh_rest = np.zeros((nfreqs, nreals, nloudest))
_Sh_rest(hc_ss, hc_bg, freqs, nexcl, nreals, nfreqs, nloudest, Sh_rest)
return Sh_rest


cdef void _Sh_rest(
double[:,:,:] hc_ss, double[:,:,] hc_bg, double[:] freqs, long nexcl,
long nreals, long nfreqs, long nloudest,
double[:,:,:] Sh_rest):
"""
Calculate the noise from all the single sources except the source in question
and the next N_excl loudest sources.
Parameters
----------
hc_ss : (F,R,L) NDarray
Characteristic strain from all loud single sources.
hc_bg : (F,R) NDarray
Characteristic strain from all but loudest source at each frequency.
freqs : (F,) 1Darray
Frequency bin centers.
nexcl : int
Number of loudest single sources to exclude from hc_rest noise, in addition
to the source in question.
Returns
-------
void
Sh_rest : (F,R,L) NDarray of scalars, updated via memory address
Sh = hc^2 / (f^3 12 pi^2)
"""
cdef int ff, rr, ll, count
cdef double Sh_ss, Sh_bg

for ff in range(nfreqs):
freq = freqs[ff]
for rr in range(nreals):
for ll in range(nloudest): # calculating for the llth loduest
Sh_ss = 0
count = 0
for ii in range(nloudest): # adding other loudest with index ii
if (ii != ll): # check it's not our current source
# if current is in top N_excl, must be (N_excl+1)th or above
# if current is >= top N_excl, must be (N_excl)th or above
if ((ll < nexcl) and (ii > nexcl)) or ((ll >= nexcl) and (ii > nexcl-1)):
Sh_ss += pow(hc_ss[ff,rr,ii], 2.0) / pow(freq, 3.0) / (12*pow(M_PI, 2.0))
count += 1
Sh_bg = pow(hc_bg[ff,rr], 2.0) / pow(freq, 3.0) / (12*pow(M_PI, 2.0))
Sh_rest[ff,rr,ll] = Sh_rest[ff,rr,ll] + Sh_ss + Sh_bg
if count != (nloudest - nexcl - 1):
err = (f"ERROR in calculate Sh_rest! count of sources={count}")
print(err)



Loading

0 comments on commit 533932e

Please sign in to comment.