def fake_focalplane(): """Create a set of fake detectors. This generates 7 pixels (14 dets) in a hexagon layout at the boresight and with a made up polarization orientation. Args: None Returns: (dict): dictionary of detectors and their properties. """ zaxis = np.array([0, 0, 1.0]) samplerate = 20.0 epsilon = 0.0 net = 1.0 fmin = 0.0 alpha = 1.0 fknee = 0.05 fwhm = 30.0 npix = 7 ndet = 2 * npix pol_A = hex_pol_angles_qu(npix) pol_B = hex_pol_angles_qu(npix, offset=90.0) dets_A = hex_layout(npix, 3.0, "", "", pol_A) dets_B = hex_layout(npix, 3.0, "", "", pol_B) dets = dict() for p in range(npix): pstr = "{:01d}".format(p) for d, layout in zip(["A", "B"], [dets_A, dets_B]): props = dict() props["quat"] = layout[pstr]["quat"] props["epsilon"] = epsilon props["rate"] = samplerate props["alpha"] = alpha props["NET"] = net props["fmin"] = fmin props["fknee"] = fknee props["fwhm_arcmin"] = fwhm dname = "{}{}".format(pstr, d) dets[dname] = props return dets
def fake_focalplane(): """ Make a fake focalplane geometry. This function returns a fake hexagonal focalplane with 19 pixels and 2 detectors at each position. The spacing is set to fill the field of view. """ npix = 19 # 5' beam fwhm = 5.0 / 60.0 # 5 degree FOV... fov = 5.0 # ...converted to size of hexagon angwidth = fov * np.cos(30.0 * np.pi / 180.0) # Alternating polarization orientations Apol = tt.hex_pol_angles_qu(npix, offset=0.0) Bpol = tt.hex_pol_angles_qu(npix, offset=90.0) # Build a simple hexagon layout Adets = tt.hex_layout(npix, 100.0, angwidth, fwhm, "fake_", "A", Apol) Bdets = tt.hex_layout(npix, 100.0, angwidth, fwhm, "fake_", "B", Bpol) # Combine into a single dictionary dets = Adets.copy() dets.update(Bdets) # Give each detector the same fake noise properties for indx, d in enumerate(sorted(dets.keys())): # 50mHz knee frequency dets[d]["fknee"] = 0.050 # High pass "plateau" to avoid blow-up at very low f dets[d]["fmin"] = 1.0e-5 # Exponent dets[d]["alpha"] = 1.0 # Sensitivity dets[d]["NET"] = 20.0e-6 # Unique index for reproducibility of simulations dets[d]["index"] = indx return dets
def main(): parser = argparse.ArgumentParser( description="Simulate fake hexagonal focalplane.", fromfile_prefix_chars='@') parser.add_argument("--minpix", required=False, type=int, default=100, help="minimum number of pixels to use") parser.add_argument("--out", required=False, default="fp_fake", help="Root name of output pickle file") parser.add_argument("--fwhm", required=False, type=float, default=5.0, help="beam FWHM in arcmin") parser.add_argument("--fwhm_sigma", required=False, type=float, default=0, help="Relative beam FWHM distribution width") parser.add_argument("--fov", required=False, type=float, default=5.0, help="Field of View in degrees") parser.add_argument("--psd_fknee", required=False, type=float, default=0.05, help="Detector noise model f_knee in Hz") parser.add_argument("--psd_NET", required=False, type=float, default=60.0e-6, help="Detector noise model NET in K*sqrt(sec)") parser.add_argument("--psd_alpha", required=False, type=float, default=1.0, help="Detector noise model slope") parser.add_argument("--psd_fmin", required=False, type=float, default=1.0e-5, help="Detector noise model f_min in Hz") parser.add_argument("--bandcenter_ghz", required=False, type=float, help="Band center frequency [GHz]") parser.add_argument("--bandcenter_sigma", required=False, type=float, default=0, help="Relative band center distribution width") parser.add_argument("--bandwidth_ghz", required=False, type=float, help="Bandwidth [GHz]") parser.add_argument("--bandwidth_sigma", required=False, type=float, default=0, help="Relative bandwidth distribution width") parser.add_argument("--random_seed", required=False, type=np.int, default=123456, help="Random number generator seed for randomized " "detector parameters") args = timing.add_arguments_and_parse(parser, timing.FILE(noquotes=True)) # Guard against being called with multiple processes if MPI.COMM_WORLD.rank == 0: # Make one big hexagon layout at the center of the focalplane. # Compute the number of pixels that is at least the number requested. test = args.minpix - 1 nrings = 0 while (test - 6 * nrings) > 0: test -= 6 * nrings nrings += 1 npix = 1 for r in range(1, nrings + 1): npix += 6 * r print("using {} pixels ({} detectors)".format(npix, npix * 2)) # Translate the field-of-view into distance between flag sides angwidth = args.fov * np.cos(30 * degree) Apol = tt.hex_pol_angles_qu(npix, offset=0.0) Bpol = tt.hex_pol_angles_qu(npix, offset=90.0) Adets = tt.hex_layout(npix, angwidth, "fake_", "A", Apol) Bdets = tt.hex_layout(npix, angwidth, "fake_", "B", Bpol) dets = Adets.copy() dets.update(Bdets) np.random.seed(args.random_seed) for indx, d in enumerate(sorted(dets.keys())): dets[d]["fknee"] = args.psd_fknee dets[d]["fmin"] = args.psd_fmin dets[d]["alpha"] = args.psd_alpha dets[d]["NET"] = args.psd_NET # This is in degrees, but the input is in arcmin. dets[d]["fwhm_deg"] = (args.fwhm / 60.0) \ * (1 + np.random.randn()*args.fwhm_sigma) # This is a fixed value, in arcmin. dets[d]["fwhm"] = args.fwhm if args.bandcenter_ghz: dets[d]["bandcenter_ghz"] \ = args.bandcenter_ghz * (1+np.random.randn()*args.bandcenter_sigma) if args.bandwidth_ghz: dets[d]["bandwidth_ghz"] \ = args.bandwidth_ghz * (1+np.random.randn()*args.bandwidth_sigma) dets[d]["index"] = indx outfile = "{}_{}".format(args.out, npix) qdets = {x: y["quat"] for x, y in dets.items()} beams = {x: (60.0 * y["fwhm_deg"]) for x, y in dets.items()} tt.plot_focalplane(qdets, args.fov, args.fov, "{}.png".format(outfile), fwhm=beams) with open("{}.pkl".format(outfile), "wb") as p: pickle.dump(dets, p) return
def fake_focalplane( samplerate: float = 20., epsilon: float = 0., net: float = 1., fmin: float = 0., alpha: float = 1., fknee: float = 0.05, fwhm: float = 30., npix: int = 7, fov: float = 3., ) -> Dict[str, Union[float, np.ndarray]]: """Create a set of fake detectors. This generates 7 pixels (14 dets) in a hexagon layout at the boresight and with a made up polarization orientation. This function is copied from TOAST workshop with the following license: BSD 2-Clause License Copyright (c) 2019, hpc4cmb All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ pol_A = hex_pol_angles_qu(npix) pol_B = hex_pol_angles_qu(npix, offset=90.0) dets_A = hex_layout(npix, fov, "", "", pol_A) dets_B = hex_layout(npix, fov, "", "", pol_B) dets = {} for p in range(npix): pstr = "{:01d}".format(p) for d, layout in zip(["A", "B"], [dets_A, dets_B]): props = dict() props["quat"] = layout[pstr]["quat"] props["epsilon"] = epsilon props["rate"] = samplerate props["alpha"] = alpha props["NET"] = net props["fmin"] = fmin props["fknee"] = fknee props["fwhm_arcmin"] = fwhm dname = "{}{}".format(pstr, d) dets[dname] = props return dets
#!/usr/bin/env python3 from toast.mpi import MPI from toast.tod.sim_tod import TODSatellite from toast.tod import hex_layout, hex_pol_angles_qu # Create a simple hex focalplane. This number of detectors might be typical # for the number per process in a full-machine run. npix = 37 pol = hex_pol_angles_qu(npix) fp = hex_layout(npix, 1.0, "det", "", pol) print(fp, flush=True) # Write this out to a simple text format with open("focalplane.txt", "w") as f: for det, props in fp.items(): q = props["quat"] f.write("{} {:0.15e} {:0.15e} {:0.15e} {:0.15e}\n".format( det, q[0], q[1], q[2], q[3])) # Use a typical sample rate and simulate data for one observation. We can # then repeat this for a typical number of observations to get the right # data volume. rate = 100.0 samples = 180000 tod = TODSatellite(MPI.COMM_WORLD, fp, samples,
npix = 1 for r in range(1, nrings + 1): npix += 6 * r print("using {} pixels ({} detectors)".format(npix, npix * 2)) fwhm = args.fwhm / 60.0 width = 100.0 # Translate the field-of-view into distance between flag sides angwidth = args.fov * np.cos(30 * degree) Apol = tt.hex_pol_angles_qu(npix, offset=0.0) Bpol = tt.hex_pol_angles_qu(npix, offset=90.0) Adets = tt.hex_layout(npix, width, angwidth, fwhm, "fake_", "A", Apol) Bdets = tt.hex_layout(npix, width, angwidth, fwhm, "fake_", "B", Bpol) dets = Adets.copy() dets.update(Bdets) np.random.seed(args.random_seed) for indx, d in enumerate(sorted(dets.keys())): dets[d]["fknee"] = args.psd_fknee dets[d]["fmin"] = args.psd_fmin dets[d]["alpha"] = args.psd_alpha dets[d]["NET"] = args.psd_NET dets[d]["fwhm_deg"] = fwhm \ * (1 + np.random.randn()*args.fwhm_sigma) dets[d]["fwhm"] = dets[d]["fwhm_deg"] # Support legacy code
def main(): parser = argparse.ArgumentParser( description="Simulate fake hexagonal focalplane.", fromfile_prefix_chars='@') parser.add_argument( "--minpix", required=False, type=int, default=100, help="minimum number of pixels to use" ) parser.add_argument( "--out", required=False, default="fp_fake", help="Root name of output pickle file" ) parser.add_argument( "--fwhm", required=False, type=float, default=5.0, help="beam FWHM in arcmin" ) parser.add_argument( "--fwhm_sigma", required=False, type=float, default=0, help="Relative beam FWHM distribution width" ) parser.add_argument( "--fov", required=False, type=float, default=5.0, help="Field of View in degrees" ) parser.add_argument( "--psd_fknee", required=False, type=float, default=0.05, help="Detector noise model f_knee in Hz" ) parser.add_argument( "--psd_NET", required=False, type=float, default=60.0e-6, help="Detector noise model NET in K*sqrt(sec)" ) parser.add_argument( "--psd_alpha", required=False, type=float, default=1.0, help="Detector noise model slope" ) parser.add_argument( "--psd_fmin", required=False, type=float, default=1.0e-5, help="Detector noise model f_min in Hz" ) parser.add_argument( "--bandcenter_ghz", required=False, type=float, help="Band center frequency [GHz]" ) parser.add_argument( "--bandcenter_sigma", required=False, type=float, default=0, help="Relative band center distribution width" ) parser.add_argument( "--bandwidth_ghz", required=False, type=float, help="Bandwidth [GHz]" ) parser.add_argument( "--bandwidth_sigma", required=False, type=float, default=0, help="Relative bandwidth distribution width" ) parser.add_argument( "--random_seed", required=False, type=np.int, default=123456, help="Random number generator seed for randomized " "detector parameters" ) args = timing.add_arguments_and_parse(parser, timing.FILE(noquotes=True)) # Guard against being called with multiple processes if MPI.COMM_WORLD.rank == 0: # Make one big hexagon layout at the center of the focalplane. # Compute the number of pixels that is at least the number requested. test = args.minpix - 1 nrings = 0 while (test - 6 * nrings) > 0: test -= 6 * nrings nrings += 1 npix = 1 for r in range(1, nrings+1): npix += 6 * r print("using {} pixels ({} detectors)".format(npix, npix*2)) # Translate the field-of-view into distance between flag sides angwidth = args.fov * np.cos(30 * degree) Apol = tt.hex_pol_angles_qu(npix, offset=0.0) Bpol = tt.hex_pol_angles_qu(npix, offset=90.0) Adets = tt.hex_layout(npix, angwidth, "fake_", "A", Apol) Bdets = tt.hex_layout(npix, angwidth, "fake_", "B", Bpol) dets = Adets.copy() dets.update(Bdets) np.random.seed(args.random_seed) for indx, d in enumerate(sorted(dets.keys())): dets[d]["fknee"] = args.psd_fknee dets[d]["fmin"] = args.psd_fmin dets[d]["alpha"] = args.psd_alpha dets[d]["NET"] = args.psd_NET # This is in degrees, but the input is in arcmin. dets[d]["fwhm_deg"] = (args.fwhm / 60.0) \ * (1 + np.random.randn()*args.fwhm_sigma) # This is a fixed value, in arcmin. dets[d]["fwhm"] = args.fwhm if args.bandcenter_ghz: dets[d]["bandcenter_ghz"] \ = args.bandcenter_ghz * (1+np.random.randn()*args.bandcenter_sigma) if args.bandwidth_ghz: dets[d]["bandwidth_ghz"] \ = args.bandwidth_ghz * (1+np.random.randn()*args.bandwidth_sigma) dets[d]["index"] = indx outfile = "{}_{}".format(args.out, npix) qdets = { x : y["quat"] for x, y in dets.items() } beams = { x : (60.0*y["fwhm_deg"]) for x, y in dets.items() } tt.plot_focalplane(qdets, args.fov, args.fov, "{}.png".format(outfile), fwhm=beams) with open("{}.pkl".format(outfile), "wb") as p: pickle.dump(dets, p) return