def get_alms_iso(events, l_max): cplx_alms = np.zeros(Alm.getsize(l_max), dtype=complex) for l in xrange(l_max + 1): for m in xrange(l + 1): cplx_alms[Alm.getidx(l_max, l, m)] = np.mean( cplxYlm(l, m, events["theta"], events["phi"])) return cplx_alms
def cplx2real_alms(cplx_alms): """ convert healpy's complex alms to denton's real alms """ l_max = Alm.getlmax(len(cplx_alms)) real_alms = np.zeros((l_max + 1)**2) for i in xrange(len(cplx_alms)): l, m = Alm.getlm(l_max, i) if m == 0: real_alms[lm2i(l, m)] = cplx_alms[i].real else: real_alms[lm2i(l, m)] = np.sqrt(2) * cplx_alms[i].real real_alms[lm2i(l, -m)] = -(-1)**m * np.sqrt(2) * cplx_alms[i].imag return real_alms
def real2cplx_alms(real_alms): """ convert denton's real alms to healpy's complex alms """ l_max = np.sqrt(len(real_alms)) - 1 assert l_max == int(l_max) l_max = int(l_max) cplx_alms = np.zeros(Alm.getsize(l_max), dtype=complex) for i in xrange(len(real_alms)): l, m = i2lm(i) if m == 0: cplx_alms[Alm.getidx(l_max, l, m)] = real_alms[i] if m < 0: cplx_alms[Alm.getidx( l_max, l, abs(m))] += -(-1)**m * 1j * real_alms[i] / np.sqrt(2) if m > 0: cplx_alms[Alm.getidx(l_max, l, m)] += real_alms[i] / np.sqrt(2) return cplx_alms
def get_alms_exposure(events, l_max, ff): # this is the prefered way # 1702.07209 eq 8 # same as Sommers omegas = CR.omega(events["dec"], ff) # check field names in events phi_field = "phi" if phi_field not in events.dtype.names: names = list(events.dtype.names) idx = names.index("RA") names[idx] = phi_field events.dtype.names = names exposure_alms = np.zeros(Alm.getsize(l_max), dtype=complex) for l in xrange(l_max + 1): for m in xrange(l + 1): cplx_alms = cplxYlm(l, m, CR.dec2theta(events["dec"]), events["phi"]) / omegas exposure_alms[Alm.getidx(l_max, l, m)] = np.sum(cplx_alms) return exposure_alms / (exposure_alms[0] * np.sqrt(4 * np.pi) ) # normalize the alms so that a00 = 1/sqrt(4pi)
def swhtWorker(idx, lProc, lMax, kRVec, kZeroBool, inputDropped, phi, theta, preFac, results, uvw): """SWHT implementatino for full sky images. Based on the methodology described by Carrozi 2015 Args: idx (int): Multiprocessing ID lProc (int or list-like): l Values to process (list or upto l) lMax (int): Maximum overall l value (for MP reference) kRVec (np.ndarray): Product of k . r for reference kZeroBool (np.ndarray): Locations of r = 0 to fix issues inputDropped (np.ndarray): Input correlations, named as we tend to drop the lower triangle for SWHT observations phi (np.ndarray): Element of UVW plane in psh. coords theta (np.ndarray): Element of UVW plane in psh. coords preFac (float): Constant for output results (np.ndarray): Reference expected output map shape uvw (np.ndarray): Reference for shapes, can be removed if a better reference is found. Returns: idx, outputHealPyMap: Multiprocessing ID, resulting healpy map """ # Multiprocessing gives a list, single process gives an int. if isinstance(lProc, int): iterL = range(lProc) else: iterL = lProc for counter, l in enumerate(iterL): print("Processing l value {0} ({1}/{2}) on Chunk {3}.".format(l, counter + 1, len(iterL), idx + 1)) j_l = scipy.special.spherical_jn(l, kRVec) # (rLen,) j_l[kZeroBool] = 0. # nan otherwise lRev = (4 * np.pi * (-1j)**l) # (1,) visBessel = inputDropped.flatten() * np.repeat(j_l, uvw.shape[0], axis = 0) # Healpy dumps m < 0, so don't both calculating them. for m in range(l + 1): y_lm_star = np.conj(scipy.special.sph_harm(m, l, phi.T.flatten(), theta.T.flatten()))# (timeSamples * nants ** 2) resultsIndex = almMap.getidx(lMax - 1, l, m) results[resultsIndex] = preFac * np.sum(visBessel * y_lm_star) / lRev print("Chunk {0} work completed for l values {1}".format(idx + 1, iterL)) return idx, results
def fname2real_alms(fname, l_max): the_map = np.genfromtxt(fname) the_map = hp.pixelfunc.ud_grade(the_map, 128, pess=True, power=1.) the_map /= the_map.sum() the_map = hp.smoothing(the_map, sigma=np.deg2rad(2), verbose=False) the_map -= 1. * np.min(the_map) the_map /= the_map.sum() # the_map[hp.pixelfunc.ang2pix(128, np.pi/2 - 0.222, 3.26)] += 0.1 # virgo cluster in equatorial coordinates # the_map[hp.pixelfunc.ang2pix(128, 1.23, 5.4)] += 0.1 # cen a in galactic coordinates cplx_alms = hp.sphtfunc.map2alm(the_map, l_max) cplx_alms /= cplx_alms[Alm.getidx(l_max, 0, 0)] cplx_alms /= np.sqrt(4 * np.pi) # hp.visufunc.mollview(hp.sphtfunc.alm2map(cplx_alms, 128)) # plt.show() real_alms = cplx2real_alms(cplx_alms) return real_alms
def swhtProcessCorrelations(corrDict, options, processDict, xyz, stationLocation, metaDataArr, frequency, labelOptions): """Handler for converting an observation to a full sky map through the spherical wave harmonic transform. Args: corrDict (dict): Dictionary of elements in the format 'CORRELATION': (nAnts x nAnts x nSamples array of correlations) options (dict): Standard options dictionary processDict (dict): Dictionary of correlation times ('XX', 'XY'...) and values of whether or not to process them xyz (np.ndarray): Antenna locations nAnts x 3 stationLocation (list): Station location in (lon (deg), lat (deg) and elevation (meters)) metaDataArr (list): List of dictionaries formatted as strings from the input dataset attributes frequency (float): Observing frequency (in Hz) labelOptions (list): List of useful values for plotting Returns: dict: Dictionary of processed outputs ('XX', 'I',....) """ # Borrowed from Griffith's method as an optomisisation, cuts processed values by a factor of 2. # Should look into implementing in the FT method? After all, we can recreate the lost correlations # by getting their complex conjugate. xyz = xyz - xyz.transpose(1,0,2) xyzTriu = np.triu_indices(xyz.shape[0]) xyz = xyz[xyzTriu] # Extract the integration midpoints from the metadata array. dateArr = [dateTime.split("'integrationMidpoint': '")[1].split("', ")[0] for dateTime in metaDataArr] # We need to use two sets of locations: The true location for the zenith ponting and an arbitrary lon/lat for the uvw plane. trueTelescopeLoc = astropy.coordinates.EarthLocation( lat = stationLocation[1] * u.deg, lon = stationLocation[0] * u.deg, height = stationLocation[2] * u.m ) telescopeLoc = astropy.coordinates.EarthLocation(lat = 0 * u.deg, lon = -90 * u.deg, height = 0 * u.m) # Not sure why, used by Griffin as a baseline for calculations and it works. # Initialise the UVW array uvw = np.zeros([0] + list(xyz.shape)) zenithArr = [] timeArr = [] for timestamp in dateArr: # Get two sets of times: One for getting the zenith, the other for plotting the observation title times in local time obsTime = astropy.time.Time(timestamp, scale = 'utc', location = telescopeLoc) utcObsTime = astropy.time.Time(timestamp, scale = 'utc') altAz = astropy.coordinates.AltAz(az = 0. * u.deg, alt = 90. * u.deg, location = trueTelescopeLoc, obstime = utcObsTime) zenith = altAz.transform_to(astropy.coordinates.Galactic) sidAngle = float(obsTime.sidereal_time('mean').radian) zenithArr.append(zenith) timeArr.append(obsTime) # Rotate the baselines to generate a uvw-like plane (still needs to be divded by wavelength) sinSA = np.sin(sidAngle) cosSA = np.cos(sidAngle) rotationMatrix = np.array([[sinSA, cosSA, 0.], [-1. * cosSA, sinSA, 0.], [0., 0., 1.]]) uvw = np.concatenate([uvw, np.dot(rotationMatrix, xyz.T).T[np.newaxis]], axis = 0) print('UVW Successfully generated for timestamp {0} with zenith at {1:.2f}, {2:.2f} (GAL)'.format(timestamp[:-5], zenith.l.deg, zenith.b.deg)) # Convert the UVW coordinates to r, theta, phi (referred to as rtp) for spherical coordinates r, theta, phi = cartToSpherical(uvw) r = r[0] # All samples have the same r values # Generate the k-vector to normalise the r elements k_0 = 2. * np.pi * frequency / scipy.constants.c preFac = 2. * (k_0 ** 2) / np.pi kRVec = k_0 * r # Get the maximum l value from the options dictionary and prepare the result arrays lMax = options['imagingOptions']['swhtlMax'] arraySize = almMap.getsize(lMax - 1) results = np.zeros(arraySize, dtype = complex) maskVar = np.ones(healpy.nside2npix(max(64, 2 * lMax)), dtype=np.bool) # Account for autocorrelations (remove excess np.nans from results as r = 0 causes issues to say the least) kZeroBool = kRVec == 0. # Generate the mask based on the pointings: pixels are masked if they are never within 90 degrees of a zenith pointing if options['imagingOptions']['maskOutput']: pixelTheta, pixelPhi = healpy.pix2ang(max(64, 2 * lMax), np.arange(healpy.nside2npix(max(64, 2 * lMax))), lonlat = True) # F**k everyhting about this coordinate system... # For further context: standard latitutde and longitude, healpy and galactic coordinates all have different # ways of wrapping angles. I created functions to map between the different coordinate systems pixelTheta = usefulFunctions.lonToHealpyLon(pixelTheta, rads = False) galCoordLon = np.array([skyCoord.l.deg for skyCoord in zenithArr]) galCoordLat = np.array([skyCoord.b.deg for skyCoord in zenithArr]) # Convert to radians for easier angular maths galCoordSampled = np.deg2rad(np.vstack([galCoordLon, galCoordLat])[:, np.newaxis]) pixelCoord = np.deg2rad(np.vstack([pixelTheta, pixelPhi])[..., np.newaxis]) # Check where the angular difference is less than 70 (ie., 35 degrees in any direction., save as used for all sky maps) # Not sure about where the factor of 2 is coming from, but I can visually confirm that this works. deltaLoc = greatCircleAngularDiff(galCoordSampled, pixelCoord) maskVar = np.logical_not(np.any(np.rad2deg(deltaLoc) < 35, axis = 1)) procDict = {} # Create a rotate to convert from arbitrary healpy logic to cartesian-galactic coordinates galRotator = healpy.rotator.Rotator(coord = ['C','G']) for key, value in processDict.items(): # processDict is a dictionary of keys/values of correlations and whether or not they are needed for the output images # So if we were imaging the Stokes I, we would have {'XX': True, 'YY': True, 'XY': False,...}, making it easy to only processed # the required correlations. if value: # Drop the autocorrelations; we have to remove the r = 0 elements to get rid of inf/nans corrDict[key][np.eye(corrDict[key].shape[0], dtype = bool)] = 0. inputDropped = corrDict[key][xyzTriu] # Multithreaded, might not be more efficient if I implement Schaeffer 2015 if options['multiprocessing']: processCount = int(mp.cpu_count()-1) mpPool = mp.Pool(processes = processCount) fragments = [np.arange(lMax)[i::processCount] for i in range(processCount)] callBacks = [mpPool.apply_async(swhtWorker.swhtWorker, args = ([idx, fragmentChunk, lMax, kRVec, kZeroBool, inputDropped, phi, theta, preFac, results, uvw])) for idx, fragmentChunk in enumerate(fragments)] mpPool.close() mpPool.join() for asyncResult in callBacks: idx, resultsVar = asyncResult.get() results[resultsVar != 0.] = resultsVar[resultsVar != 0.] allSkyImageAlm = results else: allSkyImageAlm = swhtWorker.swhtWorker(0, lMax, lMax, kRVec, kZeroBool, inputDropped, phi, theta, preFac, results, uvw)[1] # Convert the results to a healpy map procDict['{0}-alm'.format(key)] = allSkyImageAlm hpMap = healpy.alm2map(allSkyImageAlm, max(64, 2 * lMax)).astype(complex) # Clone + generate imaginary map as well (as healpy seems to hate imaginary values results) # Not sure if this is the right way to go about doing things, but it seems to function at least. # Can't find a Stokes-V map of the sky to compare. clone = allSkyImageAlm.copy() clone.real = clone.imag clone.imag = np.zeros_like(allSkyImageAlm) hpMap.imag = healpy.alm2map(clone, max(64, 2 * lMax)) # Rotate to the right coordinate system hpMap = galRotator.rotate_map(hpMap) # Store the results] print("{0} Polarisation Processed.".format(key)) procDict[key] = hpMap # Process each of the requested outputs for method in options['imagingOptions']['correlationTypes']: if len(method) == 2: allSkyImage = procDict[method].real allSkySave = procDict[method] elif 'I' is method: allSkySave = (procDict['XX'] + procDict['YY']) allSkyImage = allSkySave.real elif 'Q' is method: allSkySave = (procDict['XX'] - procDict['YY']) allSkyImage = allSkySave.real elif 'U' is method: allSkySave = (procDict['XY'] + procDict['YX']) allSkyImage = allSkySave.real elif 'V' is method: # Healpy doesn't like dealing with imaginary values, take the 'V' output with a mountain of salt. allSkySave = (procDict['YX'] - procDict['XY']) allSkyImage = allSkySave.imag else: print('How did we get this far with a broken method \'{0}\'? Processing the first value in procDict so it hasn\'t gone to waste...'.format(method)) allSkyImage = procDict.values()[0] method = procDict.keys()[0] # Mask off values if needed. # For note, doing it before this point breaks stuff as the masked arrays fail to mask on simple maths (values on the other of 1e40 anyone?) allSkyImage = healpy.ma(allSkyImage) allSkySave = healpy.ma(allSkySave) allSkyImage.mask = maskVar allSkySave.mask = maskVar # Don't save the result out of we are doing a pure-correlation map procDict[method] = allSkySave if options['plottingOptions']['plotImages']: swhtPlot(allSkyImage, options, labelOptions + [method, 0], healpy.newvisufunc.mollview, [timeArr, trueTelescopeLoc], zenithArr, metaDataArr) # Save a copy of the mask encase it gets mangled again. procDict['mask'] = maskVar return procDict
def get_alms_iso_exact(l_max): assert l_max == int(l_max) return np.zeros(Alm.getsize(l_max), dtype=complex)
def lmax(self, l): self._almsize = Alm.getsize(l - 1) self._hpsize = max(128, 2 * l) #max(64, 2 * l) self._max = l return