def get_disk(mask, pos_src, out_edge, in_edge, tol=1): ''' Return unmasked pixel indices of disks centred at a source. For usage purpose the input includes two disks. Input: mask: mask healpix map; pos_src: position of source (with shape=3); out_edge: outer disk centred at each source(in arcmin); in_edge: inner disk centred at each source(in arcmin); tol: tolerance between 0 and 1 indicating a threshold of fraction of unmasked pixels in a disk Output: pixel indices of both disks. ''' Ns = hp.npix2nside(mask.size) list_out = hp.query_disc(Ns, pos_src, np.radians(out_edge / 60.)) npix_out = len(list_out) list_out_unmasked = list_out[mask[list_out] > 0] if (list_out_unmasked.size < tol * npix_out): return [0, 0] lon, lat = hp.vec2dir(pos_src, lonlat=True) list_in = hp.query_disc(Ns, pos_src, np.radians(in_edge / 60.)) list_in_unmasked = list_in[mask[list_in] > 0] return list_out_unmasked, list_in_unmasked
def get_disk(self, src_ind, out_edge, in_edge, tol=1): ''' Return unmasked pixel indices of disks centred at a source. For usage purpose the input includes two disks. Input: src_ind: index of source; out_edge: outer disk centred at each source(in arcmin); in_edge: inner disk centred at each source(in arcmin); tol: tolerance between 0 and 1 indicating a threshold of fraction of unmasked pixels in a disk Output: pixel indices of both disks. ''' mask = self.mask Ns = self.Nside pos_src = self.pos_src_all[src_ind] list_out = hp.query_disc(Ns, pos_src, np.radians(out_edge / 60.)) npix_out = len(list_out) list_out_unmasked = list_out[mask[list_out] > 0] if(list_out_unmasked.size < tol * npix_out): return [0, 0] lon, lat = hp.vec2dir(pos_src, lonlat=True) list_in = hp.query_disc(Ns, pos_src, np.radians(in_edge / 60.)) list_in_unmasked = list_in[mask[list_in] > 0] return list_out_unmasked, list_in_unmasked
def generate_point_set(self) -> Tuple[np.ndarray, np.ndarray]: rng = np.random.default_rng(self.seed) lon = 360 * rng.random(size=self.resolution) - 180 uniform = rng.random(size=self.resolution) lat = np.arcsin(2 * uniform - 1) * 180 / np.pi vec = hp.dir2vec(theta=lon, phi=lat, lonlat=True) dir = hp.vec2dir(vec, lonlat=self.lonlat) return vec, dir
def generate_point_set(self) -> Tuple[np.ndarray, np.ndarray]: step = np.arange(self.resolution) phi = step * self.golden_angle phi = Angle(phi * u.rad).wrap_at(2 * np.pi * u.rad) theta = np.arccos(1 - (2 * step / self.resolution)) vec = hp.dir2vec(theta=theta, phi=phi, lonlat=False) dir = hp.vec2dir(vec, lonlat=self.lonlat) return vec, dir
def get_tiling(lon0, lat0, dlon, dlat): rot = hp.rotator.Rotator(rot=(0., -lat0, -lon0), eulertype='Y', deg=True).mat x = hp.dir2vec(dlon, dlat, lonlat=True) if hasattr(dlon, '__len__') and (len(x.shape) == 1): x.shape = (3, 1) y = np.einsum('ij,jn', rot, x) lonlat = hp.vec2dir(y, lonlat=True) return lonlat
def get_tiling(lon0, lat0, dlon, dlat): rot = hp.rotator.Rotator(rot=(0., -lat0, -lon0), eulertype='Y', deg=True).mat x = hp.dir2vec(dlon, dlat, lonlat=True) if hasattr(dlon, '__len__') and (len(x.shape) == 1): x.shape = (3,1) y = np.einsum('ij,jn', rot, x) lonlat = hp.vec2dir(y, lonlat=True) return lonlat
def coord_trans(pos_in, coord_in, coord_out, lonlat=False): r = hp.rotator.Rotator(coord=[coord_in, coord_out]) pos_out = r(pos_in.T).T if lonlat: if pos_out.shape[1] == 2: return pos_out elif pos_out.shape[1] == 3: return hp.vec2dir(pos_out.T, lonlat=True).T else: return pos_out
def get_2d_profile(skymap, mask, pos_src, xbins, ybins, out_edge, in_edge, tol=1): ''' Calculate the 2d signal profile(map) of a single source Input: skymap: healpix skymap; mask: mask healpix map; pos_src: position of the source (with size=3); xbins: array, bins in x direction specified by the user. ybins: array, bins in y direction specified by the user. out_edge: outer disk of the ring to calculate background(in arcmin); in_edge: inner disk of the ring to calculate background(in arcmin); tol: tolerance between 0 and 1 indicating a threshold of fraction of unmasked pixels in a disk Output: 2d signal profile(map) of a single source ''' list_out_unmasked, list_in_unmasked = get_disk(mask, pos_src, out_edge, in_edge, tol) bkg = get_bkg(skymap, mask, list_out_unmasked, list_in_unmasked) xybin = np.transpose( [np.tile(xbins, len(ybins)), np.repeat(ybins, len(xbins))]) Ns = hp.npix2nside(mask.size) nbins = xbins.size x, y = xybin.T lon, lat = hp.vec2dir(pos_src, lonlat=True) lat_pix = y / 60. + lat # in degrees lon_pix = x / 60. / np.cos(np.radians(lat)) + lon try: x_pix, y_pix, z_pix = hp.ang2vec(lon_pix, lat_pix, lonlat=True).T except ValueError, e: print(e) return np.zeros((nbins, nbins))
def main(): parser = argparse.ArgumentParser( description="This program measures the median offset of subset of " "detectors from boresight.", usage="get_wafer_offset [options] (use --help for details)") group = parser.add_mutually_exclusive_group(required=True) group.add_argument( "--tube_slots", help="Comma-separated list of optics tube slots: c1 (UHF), i5 (UHF), " " i6 (MF), i1 (MF), i3 (MF), i4 (MF), o6 (LF). ") group.add_argument("--wafer_slots", help="Comma-separated list of optics tube slots. ") parser.add_argument("--reverse", action="store_true", help="Reverse offsets") # LAT specific params parser.add_argument( "--corotate-lat", required=False, action="store_true", help="Rotate LAT receiver to maintain focalplane orientation", dest="corotate_lat", ) parser.add_argument( "--no-corotate-lat", required=False, action="store_false", help="Do not Rotate LAT receiver to maintain focalplane orientation", dest="corotate_lat", ) parser.set_defaults(corotate_lat=True) parser.add_argument( "--elevation-deg", required=False, type=np.float, help="Observing elevation", ) args = parser.parse_args() hw = hardware.get_example() # Which telescope? if args.wafer_slots is not None: wafer_slots = args.wafer_slots.split(",") wafer_map = hw.wafer_map() tube_slots = [wafer_map["tube_slots"][ws] for ws in wafer_slots] else: tube_slots = args.tube_slots.split(",") telescope = None for tube_slot in tube_slots: for telescope_name, telescope_data in hw.data["telescopes"].items(): if tube_slot in telescope_data["tube_slots"]: if telescope is None: telescope = telescope_name elif telescope != telescope.name: raise RuntimeError( f"Tubes '{tube_slots}' span more than one telescope") if telescope is None: raise RuntimeError( f"Failed to match tube_slot = '{tube_slot}' with a telescope") # Which detectors? hw.data["detectors"] = hardware.sim_telescope_detectors(hw, telescope) match = {} tube_slots = None if args.wafer_slots is not None: match["wafer_slot"] = args.wafer_slots.split(",") elif args.tube_slots is not None: tube_slots = args.tube_slots.split(",") hw = hw.select(tube_slots=tube_slots, match=match) ndet = len(hw.data["detectors"]) # print(f"tube_slots = {tube_slots}, match = {match} leaves {ndet} detectors") # Optional corotator rotation if telescope == "LAT": if args.corotate_lat: rot = qa.rotation(ZAXIS, np.radians(LAT_COROTATOR_OFFSET_DEG)) else: if args.elevation_deg is None: raise RuntimeError( "You must set the observing elevation when not co-rotating." ) rot = qa.rotation( ZAXIS, np.radians(args.elevation_deg - 60 + LAT_COROTATOR_OFFSET_DEG)) else: if args.elevation_deg is not None: raise RuntimeError("Observing elevation does not matter for SAT") rot = None # Average detector offset vec_mean = np.zeros(3) for det_name, det_data in hw.data["detectors"].items(): quat = det_data["quat"] if rot is not None: quat = qa.mult(rot, quat) vec = qa.rotate(quat, ZAXIS) vec_mean += vec vec_mean /= ndet # Radius all_dist = [] for det_name, det_data in hw.data["detectors"].items(): quat = det_data["quat"] if rot is not None: quat = qa.mult(rot, quat) vec = qa.rotate(quat, ZAXIS) all_dist.append(np.degrees(np.arccos(np.dot(vec_mean, vec)))) dist_max = np.amax(all_dist) # Wafers if args.tube_slots is None: wafer_slots = set(wafer_slots) else: wafer_slots = set() for tube_slot in tube_slots: wafer_slots.update(hw.data["tube_slots"][tube_slot]["wafer_slots"]) waferstring = "" for wafer_slot in sorted(wafer_slots): waferstring += f" {wafer_slot}" # Translate into Az/El offsets at el=0 rot = hp.Rotator(rot=[0, 90, 0]) vec_mean = rot(vec_mean) az_offset, el_offset = hp.vec2dir(vec_mean, lonlat=True) el_offset *= -1 if args.reverse: az_offset *= -1 el_offset *= -1 print(f"{az_offset:.3f} {el_offset:.3f} {dist_max:.3f}" + waferstring) return
nband = len(bands) site = "chile" nside = 4096 lmax = 2 * nside flavor = "noise_atmo_7splits" # cmb_r0 cmb_tensor_only_r3e-3 foregrounds noise_atmo_7splits #flavor = "noise" # cmb_r0 cmb_tensor_only_r3e-3 foregrounds noise_atmo_7splits split = 1 nsplits = 1 # Mock the ACT processing mask. This is 525 sq.deg (0.0127 fsky) npix = 12 * nside ** 2 pix = np.arange(npix) vec = hp.pix2vec(nside, pix) lon, lat = hp.vec2dir(vec, lonlat=True) mask = np.logical_and(lon > -4, lon < 40) * np.logical_and(lat > -8, lat < 4) # Deep56 is 565 sq.deg (0.0137 fsky) of which 340 sq.deg (0.00824 fsky) is usable for power spectrum estimation # ell pa1(150GHz) pa2(150GHz) pa3(150GHz) pa3(98GHz) pa3(98x150GHz) act_tt = np.genfromtxt("deep56_TT_Nl_out_210317.txt", skip_header=1).T act_ee = np.genfromtxt("deep56_EE_Nl_out_210317.txt", skip_header=1).T def map2cl(m, hits): """ good = hits > 0 ngood = np.sum(good) sorted_hits = np.sort(hits[good]) hit_lim = sorted_hits[np.int(ngood * .01)]
def generate_point_set(self) -> Tuple[np.ndarray, np.ndarray]: vec = np.stack(hp.pix2vec(self.nside, np.arange(self.resolution)), axis=0) dir = hp.vec2dir(vec, lonlat=self.lonlat) return vec, dir
len(xyz1), 1) * 20e-3 + pert_p_norm * GZK_dist_scale # END OF PERTURBED VECTOR # SAME CUTS ALSO APPLIED TO PERTURBED VECTOR xyz = xyz1 / xyz1l2norm.reshape(len(xyz1), 1) * 20e-3 + pxpypz_norm * GZK_dist_scale l2norm = np.sqrt((xyz * xyz).sum(axis=1)) xyz_norm = xyz / l2norm.reshape(len(xyz), 1) lon = np.zeros(len(xyz)) lat = np.zeros(len(xyz)) # PERTURBED pert_l2norm = np.sqrt((pert_xyz * pert_xyz).sum(axis=1)) pert_xyz_norm = pert_xyz / pert_l2norm.reshape(len(pert_xyz), 1) p_lon = np.zeros(len(pert_xyz)) p_lat = np.zeros(len(pert_xyz)) for i in range(len(xyz)): lon[i], lat[i] = hp.vec2dir(xyz[i], lonlat=True) p_lon[i], p_lat[i] = hp.vec2dir(pert_xyz[i], lonlat=True) # Test for nans (might be a result of run stoppage) tmp = np.isnan(lat) if len(lat[tmp]) > 0: num_nans = len(lat[tmp]) print("found %i nans in lat array. Exlcuding." % num_nans) lon = lon[~tmp] lat = lat[~tmp] xyz = xyz[~tmp] xyz_norm = xyz_norm[~tmp] pxpypz_norm = pxpypz_norm[~tmp] E1 = E1[~tmp] p_lon = p_lon[~tmp] p_lat = p_lat[~tmp] # Wrap coords to [0,360] if necessary
def exec(self, data): """Apply the 2D polynomial filter to the signal. Args: data (toast.Data): The distributed data. """ norder = self._order + 1 nmode = self._nmode world_comm = data.comm.comm_world if world_comm is None: world_rank = 0 else: world_rank = world_comm.rank for obs in data.obs: t0 = time() t_template = 0 t_get_norm = 0 t_apply_norm = 0 t_solve = 0 t_clean = 0 tod = obs["tod"] times = tod.local_times() # communicator for processes with the same sample range comm = tod.grid_comm_col if comm is None: comm_rank = 0 comm_size = 1 else: comm_rank = comm.rank comm_size = comm.size detectors = tod.detectors ndet = len(detectors) detector_index = {} pat = re.compile(self._pattern) ndet = 0 for det in detectors: if pat.match(det) is None: continue detector_index[det] = ndet ndet += 1 # Number of detectors may limit the number of modes we can constrain nmode = min(self._nmode, ndet) focalplane = obs["focalplane"] detector_templates = np.zeros([ndet, nmode]) mode = 0 xorders = np.zeros(nmode) yorders = np.zeros(nmode) for order in range(norder): for yorder in range(order + 1): xorder = order - yorder xorders[mode] = xorder yorders[mode] = yorder mode += 1 if mode == nmode: break if mode == nmode: break yrot = qa.rotation(YAXIS, np.pi / 2) for det in tod.local_dets: if det not in detector_index: continue idet = detector_index[det] det_quat = focalplane[det]["quat"] vec = qa.rotate(det_quat, ZAXIS) theta, phi = hp.vec2dir(qa.rotate(yrot, vec)) theta -= np.pi / 2 detector_templates[idet] = theta ** xorders * phi ** yorders if self._intervals in obs: intervals = obs[self._intervals] else: intervals = None local_intervals = tod.local_intervals(intervals) if len(local_intervals) == 0: # No intervals to filter continue common_ref = tod.local_common_flags(self._common_flag_name) # Iterate over each interval for ival in local_intervals: istart = ival.first while istart < ival.last + 1: istop = min(istart + self._buffer_length, ival.last + 1) ind = slice(istart, istop) nsample = istop - istart templates = np.zeros([ndet, nmode]) masks = np.zeros([ndet, nsample], dtype=np.bool) proj = np.zeros([nmode, nsample]) norms = np.zeros(nmode) t1 = time() for det in tod.local_dets: if det not in detector_index: continue idet = detector_index[det] ref = tod.local_signal(det, self._name)[ind] flag_ref = tod.local_flags(det, self._flag_name)[ind] flg = common_ref[ind] & self._common_flag_mask flg |= flag_ref & self._flag_mask mask = flg == 0 # We might want to remove the interval mean if the # data were not already 1D-filtered # ref -= np.mean(ref[mask]) template = detector_templates[idet] templates[idet] = template masks[idet] = mask proj += np.outer(template, ref * mask) norms += template ** 2 del ref del flag_ref t_template += time() - t1 t1 = time() if comm is not None: comm.Barrier() comm.Allreduce(MPI.IN_PLACE, templates, op=MPI.SUM) comm.Allreduce(MPI.IN_PLACE, masks, op=MPI.LOR) comm.Allreduce(MPI.IN_PLACE, proj, op=MPI.SUM) comm.Allreduce(MPI.IN_PLACE, norms, op=MPI.SUM) good = norms != 0 norms[good] = norms[good] ** -0.5 t_get_norm += time() - t1 t1 = time() templates = templates.T.copy() # nmode x ndet for mode, norm in enumerate(norms): if norm: templates[mode] *= norm proj[mode] *= norm t_apply_norm += time() - t1 t1 = time() proj = proj.T.copy() # nsample x nmode coeff = np.zeros([nsample, nmode]) masks = np.transpose(masks) # nsample x ndet for isample in range(nsample): if isample % comm_size != comm_rank: continue mask = masks[isample] templatesT = mask * templates # nmode * ndet invcov = np.dot(templatesT, templates.T) try: cov = np.linalg.inv(invcov) coeff[isample] = np.dot(cov, proj[isample]) except np.linalg.LinAlgError: coeff[isample] = 0 if comm is not None: comm.Allreduce(MPI.IN_PLACE, coeff, op=MPI.SUM) t_solve += time() - t1 t1 = time() for isample in range(nsample): if np.all(coeff[isample] == 0): common_ref[istart + isample] |= self._poly_flag_mask templates = templates.T.copy() # ndet x nmode x nsample coeff = coeff.T.copy() # nmode x nsample for det in tod.local_dets: if det not in detector_index: continue idet = detector_index[det] ref = tod.local_signal(det, self._name)[ind] for mode in range(nmode): ref -= coeff[mode] * templates[idet, mode] t_clean += time() - t1 del templates istart = istop del common_ref if world_rank == 0 or (comm_rank == 0 and self._verbose > 1): print( "Time per observation: {:.1f} s\n" " templates : {:6.1f} s\n" " get_norm : {:6.1f} s\n" " apply_norm : {:6.1f} s\n" " solve : {:6.1f} s\n" " clean : {:6.1f} s".format( time() - t0, t_template, t_get_norm, t_apply_norm, t_solve, t_clean, ), flush=True, ) return
def translate(self, quats, timestamps, psi_pol=None): """ Translate the input quaternions into planet-centric coordinates and convert the offsets to arc minutes. The quaternions ARE EXPECTED to be in galactic coordinates although adding a rotation would be straightforward. The output coordinate system is Pxx, unless either a) quats do not include the psi_pol rotation or b) psi_pol is provided in radians. translate() will then remove the psi_pol rotation from the quaternions """ if timestamps[0] > self.time[-1] or timestamps[-1] < self.time[0]: raise Exception( 'There is no overlap in the stored and provided time stamps.') if psi_pol is not None: pol_quat = qa.rotation(zaxis, -psi_pol) zquat = qa.mult(pol_quat, self.zquat) else: zquat = self.zquat my_quats = qa.mult(quats, zquat) # From X-axis to detector position # Transform the planet positions into quaternions tol = 3600. ind = np.logical_and(self.time >= timestamps[0] - tol, self.time <= timestamps[-1] + tol) nind = np.sum(ind) planettime = self.time[ind] planetglon = self.glon[ind] planetglat = self.glat[ind] planetquat = np.zeros([nind, 4]) # ZYZ rotation to put the X-axis to the planet position phi = planetglon * degree theta = -planetglat * degree psi = 0 planetquat[:, 3] = np.cos(.5 * theta) * np.cos(.5 * (phi + psi)) planetquat[:, 0] = -np.sin(.5 * theta) * np.sin(.5 * (phi - psi)) planetquat[:, 1] = np.sin(.5 * theta) * np.cos(.5 * (phi - psi)) planetquat[:, 2] = np.cos(.5 * theta) * np.sin(.5 * (phi + psi)) targetquats = qa.slerp(timestamps, planettime, planetquat) planetvec = qa.rotate(targetquats, xaxis) # Rotate the planet into Dxx frame (detector on X-axis) planetvec = qa.rotate(qa.inv(my_quats), planetvec) # The detector position relative to the planet is the inverse # of the planet coordinates in Dxx lon, lat = hp.vec2dir(planetvec.T, lonlat=True) az, el = -np.array([lon, lat]) * 60 # To arc minutes return az, el
# In[10]: #rotmat = np.hstack([ vec_north[:,0][:,np.newaxis], np.cross(vec[:,0], vec_north[:,0])[:,np.newaxis], vec[:,0][:,np.newaxis]]) # In[11]: vec_north.T[:,0][:,np.newaxis] # Need to prepend a column of zeros to `vec`, because I need to have an input with a dimension equal to 4 to declare the signature # In[12]: lon, lat= hp.vec2dir(vec[:,0], vec[:,1], vec[:,2], lonlat=True) # In[13]: q = np.empty([len(vec), 4], dtype=np.float) # In[14]: n = len(vec) for i in range(n): rotmat = np.hstack([ vec_north[i,:][:,np.newaxis], np.cross(vec[i,:], vec_north[i,:])[:,np.newaxis], vec[i,:][:,np.newaxis] ])
def main(): parser = argparse.ArgumentParser( description="This program measures the median offset of subset of " "detectors from boresight.", usage="get_wafer_offset [options] (use --help for details)") group = parser.add_mutually_exclusive_group(required=True) group.add_argument( "--tube_slots", help="Comma-separated list of optics tube slots: c1 (UHF), i5 (UHF), " " i6 (MF), i1 (MF), i3 (MF), i4 (MF), o6 (LF). ") group.add_argument("--wafer_slots", help="Comma-separated list of optics tube slots. ") parser.add_argument("--reverse", action="store_true", help="Reverse offsets") args = parser.parse_args() hw = hardware.get_example() # Which telescope? if args.wafer_slots is not None: wafer_slots = args.wafer_slots.split(",") wafer_map = hw.wafer_map() tube_slots = [wafer_map["tube_slots"][ws] for ws in wafer_slots] else: tube_slots = args.tube_slots.split(",") telescope = None for tube_slot in tube_slots: for telescope_name, telescope_data in hw.data["telescopes"].items(): if tube_slot in telescope_data["tube_slots"]: if telescope is None: telescope = telescope_name elif telescope != telescope.name: raise RuntimeError( f"Tubes '{tube_slots}' span more than one telescope") if telescope is None: raise RuntimeError( f"Failed to match tube_slot = '{tube_slot}' with a telescope") # Which detectors? hw.data["detectors"] = hardware.sim_telescope_detectors(hw, telescope) match = {} tube_slots = None if args.wafer_slots is not None: match["wafer_slot"] = args.wafer_slots.split(",") elif args.tube_slots is not None: tube_slots = args.tube_slots.split(",") hw = hw.select(tube_slots=tube_slots, match=match) ndet = len(hw.data["detectors"]) # print(f"tube_slots = {tube_slots}, match = {match} leaves {ndet} detectors") # Average detector offset vec_mean = np.zeros(3) zaxis = np.array([0, 0, 1]) for det_name, det_data in hw.data["detectors"].items(): quat = det_data["quat"] vec = qa.rotate(quat, zaxis) vec_mean += vec vec_mean /= ndet # Radius all_dist = [] for det_name, det_data in hw.data["detectors"].items(): quat = det_data["quat"] vec = qa.rotate(quat, zaxis) all_dist.append(np.degrees(np.arccos(np.dot(vec_mean, vec)))) dist_max = np.amax(all_dist) # Translate into Az/El offsets at el=0 rot = hp.Rotator(rot=[0, 90, 0]) vec_mean = rot(vec_mean) az_offset, el_offset = hp.vec2dir(vec_mean, lonlat=True) el_offset *= -1 if args.reverse: az_offset *= -1 el_offset *= -1 print(f"{az_offset:.3f} {el_offset:.3f} {dist_max:.3f}") return
def lonlat(self, value): self._lonlat = value self.dir = hp.vec2dir(self.vec, lonlat=value)