Exemple #1
0
def test_simulation_pointings_mjd(tmp_path):
    sim = lbs.Simulation(
        base_path=tmp_path / "simulation_dir",
        start_time=Time("2020-01-01T00:00:00"),
        duration_s=130.0,
    )
    fakedet = create_fake_detector()

    sim.create_observations(detectors=[fakedet],
                            num_of_obs_per_detector=2,
                            split_list_over_processes=False)

    sstr = lbs.SpinningScanningStrategy(spin_sun_angle_rad=10.0,
                                        precession_rate_hz=10.0,
                                        spin_rate_hz=0.1)
    sim.generate_spin2ecl_quaternions(scanning_strategy=sstr,
                                      delta_time_s=60.0)

    instr = lbs.InstrumentInfo(spin_boresight_angle_rad=np.deg2rad(20.0))

    for idx, obs in enumerate(sim.observations):
        pointings_and_polangle = lbs.get_pointings(
            obs,
            spin2ecliptic_quats=sim.spin2ecliptic_quats,
            detector_quats=np.array([[0.0, 0.0, 0.0, 1.0]]),
            bore2spin_quat=instr.bore2spin_quat,
        )

        filename = Path(
            __file__).parent / f"reference_obs_pointings{idx:03d}.npy"
        reference = np.load(filename, allow_pickle=False)
        assert np.allclose(pointings_and_polangle, reference)
Exemple #2
0
def test_simulation_pointings_polangle(tmp_path):
    sim = lbs.Simulation(base_path=tmp_path / "simulation_dir",
                         start_time=0.0,
                         duration_s=61.0)
    fakedet = create_fake_detector(sampling_rate_hz=50.0)

    sim.create_observations(detectors=[fakedet],
                            num_of_obs_per_detector=1,
                            split_list_over_processes=False)
    assert len(sim.observations) == 1
    obs = sim.observations[0]

    sstr = lbs.SpinningScanningStrategy(spin_sun_angle_rad=0.0,
                                        precession_rate_hz=0.0,
                                        spin_rate_hz=1.0 / 60)
    sim.generate_spin2ecl_quaternions(scanning_strategy=sstr, delta_time_s=0.5)

    instr = lbs.InstrumentInfo(spin_boresight_angle_rad=0.0)

    pointings_and_polangle = lbs.get_pointings(
        obs,
        spin2ecliptic_quats=sim.spin2ecliptic_quats,
        detector_quats=np.array([[0.0, 0.0, 0.0, 1.0]]),
        bore2spin_quat=instr.bore2spin_quat,
    )
    polangle = pointings_and_polangle[..., 2]

    # Check that the polarization angle scans every value between -π
    # and +π
    assert np.allclose(np.max(polangle), np.pi, atol=0.01)
    assert np.allclose(np.min(polangle), -np.pi, atol=0.01)

    # Simulate the generation of a report
    sim.flush()
Exemple #3
0
def test_make_bin_map_api_simulation(tmp_path):
    # We should add a more meaningful observation:
    # Currently this test just shows the interface
    sim = lbs.Simulation(base_path=tmp_path / "tut04",
                         start_time=0.0,
                         duration_s=86400.0)

    sim.generate_spin2ecl_quaternions(
        scanning_strategy=lbs.SpinningScanningStrategy(
            spin_sun_angle_rad=np.radians(30),  # CORE-specific parameter
            spin_rate_hz=0.5 / 60,  # Ditto
            # We use astropy to convert the period (4 days) in
            # minutes, the unit expected for the precession period
            precession_rate_hz=1 / (4 * u.day).to("s").value,
        ))
    instr = lbs.InstrumentInfo(name="core",
                               spin_boresight_angle_rad=np.radians(65))
    det = lbs.DetectorInfo(name="foo", sampling_rate_hz=10)
    obss = sim.create_observations(detectors=[det])
    pointings = lbs.get_pointings(
        obss[0],
        sim.spin2ecliptic_quats,
        detector_quats=[det.quat],
        bore2spin_quat=instr.bore2spin_quat,
    )

    nside = 64
    obss[0].pixind = hp.ang2pix(nside, pointings[..., 0], pointings[..., 1])
    obss[0].psi = pointings[..., 2]
    mapping.make_bin_map(obss, nside)
Exemple #4
0
def __write_complex_observation(tmp_path, use_mjd: bool):
    start_time = AstroTime("2021-01-01") if use_mjd else 0
    time_span_s = 60
    sampling_hz = 10

    sim = lbs.Simulation(
        base_path=tmp_path,
        start_time=start_time,
        duration_s=time_span_s,
    )
    scanning = lbs.SpinningScanningStrategy(
        spin_sun_angle_rad=0.785_398_163_397_448_3,
        precession_rate_hz=8.664_850_513_998_931e-05,
        spin_rate_hz=0.000_833_333_333_333_333_4,
        start_time=start_time,
    )

    spin2ecliptic_quats = scanning.generate_spin2ecl_quaternions(
        start_time, time_span_s, delta_time_s=1.0)

    instr = lbs.InstrumentInfo(
        boresight_rotangle_rad=0.0,
        spin_boresight_angle_rad=0.872_664_625_997_164_8,
        spin_rotangle_rad=3.141_592_653_589_793,
    )

    det = lbs.DetectorInfo(
        name="Dummy detector",
        sampling_rate_hz=sampling_hz,
        bandcenter_ghz=100.0,
        quat=[0.0, 0.0, 0.0, 1.0],
    )

    sim.create_observations(detectors=[det])

    obs = sim.observations[0]
    obs.tod = np.random.random(obs.tod.shape)

    obs.pointings = lbs.scanning.get_pointings(
        obs,
        spin2ecliptic_quats=spin2ecliptic_quats,
        detector_quats=[det.quat],
        bore2spin_quat=instr.bore2spin_quat,
    )

    obs.local_flags = np.zeros(obs.tod.shape, dtype="uint16")
    obs.local_flags[0, 12:15] = 1

    obs.global_flags = np.zeros(obs.tod.shape[1], dtype="uint32")
    obs.global_flags[12:15] = 15

    return obs, det, lbs.write_observations(sim=sim, subdir_name="")
Exemple #5
0
def test_simulation_two_detectors():
    sim = lbs.Simulation(start_time=0.0, duration_s=86400.0)

    # Two detectors, the second rotated by 45°
    quaternions = [
        np.array([0.0, 0.0, 0.0, 1.0]),
        np.array([0.0, 0.0, 1.0, 1.0]) / np.sqrt(2),
    ]
    fakedet1 = create_fake_detector(sampling_rate_hz=1 / 3600,
                                    quat=quaternions[0])
    fakedet2 = create_fake_detector(sampling_rate_hz=1 / 3600,
                                    quat=quaternions[1])

    sim.create_observations(
        detectors=[fakedet1, fakedet2],
        num_of_obs_per_detector=1,
        split_list_over_processes=False,
    )
    assert len(sim.observations) == 1
    obs = sim.observations[0]

    # The spacecraft stands still in L2, with no spinning nor precession
    sstr = lbs.SpinningScanningStrategy(spin_sun_angle_rad=0.0,
                                        precession_rate_hz=0.0,
                                        spin_rate_hz=0.0)
    sim.generate_spin2ecl_quaternions(sstr, delta_time_s=60.0)
    assert sim.spin2ecliptic_quats.quats.shape == (24 * 60 + 1, 4)

    instr = lbs.InstrumentInfo(spin_boresight_angle_rad=0.0)

    pointings_and_polangle = lbs.get_pointings(
        obs,
        spin2ecliptic_quats=sim.spin2ecliptic_quats,
        detector_quats=quaternions,
        bore2spin_quat=instr.bore2spin_quat,
    )

    assert pointings_and_polangle.shape == (2, 24, 3)

    assert np.allclose(pointings_and_polangle[0, :, 0],
                       pointings_and_polangle[1, :, 0])
    assert np.allclose(pointings_and_polangle[0, :, 1],
                       pointings_and_polangle[1, :, 1])

    # The ψ angle should differ by 45°
    assert np.allclose(
        np.abs(pointings_and_polangle[0, :, 2] -
               pointings_and_polangle[1, :, 2]),
        np.pi / 2,
    )
Exemple #6
0
def test_instrument_creation():
    instr = lbs.InstrumentInfo(
        name="test",
        boresight_rotangle_rad=np.deg2rad(10.0),
        spin_boresight_angle_rad=np.deg2rad(15.0),
        spin_rotangle_rad=np.deg2rad(20.0),
    )

    assert instr.name == "test"
    assert np.allclose(instr.boresight_rotangle_rad, np.deg2rad(10.0))
    assert np.allclose(instr.spin_boresight_angle_rad, np.deg2rad(15.0))
    assert np.allclose(instr.spin_rotangle_rad, np.deg2rad(20.0))

    assert np.allclose(
        instr.bore2spin_quat, np.array([-0.01137611, 0.1300295, 0.25660481, 0.9576622])
    )
Exemple #7
0
def test_simulation_pointings_still():
    sim = lbs.Simulation(start_time=0.0, duration_s=86400.0)
    fakedet = create_fake_detector(sampling_rate_hz=1 / 3600)

    sim.create_observations(detectors=[fakedet],
                            num_of_obs_per_detector=1,
                            split_list_over_processes=False)
    assert len(sim.observations) == 1
    obs = sim.observations[0]

    # The spacecraft stands still in L2, with no spinning nor precession
    sstr = lbs.SpinningScanningStrategy(spin_sun_angle_rad=0.0,
                                        precession_rate_hz=0.0,
                                        spin_rate_hz=0.0)
    sim.generate_spin2ecl_quaternions(sstr, delta_time_s=60.0)
    assert sim.spin2ecliptic_quats.quats.shape == (24 * 60 + 1, 4)

    instr = lbs.InstrumentInfo(spin_boresight_angle_rad=0.0)

    # Move the Z vector manually using the last quaternion and check
    # that it's rotated by 1/365.25 of a complete circle
    boresight = np.empty(3)
    lbs.rotate_z_vector(boresight, *sim.spin2ecliptic_quats.quats[-1, :])
    assert np.allclose(np.arctan2(boresight[1], boresight[0]),
                       2 * np.pi / 365.25)

    # Now redo the calculation using get_pointings
    pointings_and_polangle = lbs.get_pointings(
        obs,
        spin2ecliptic_quats=sim.spin2ecliptic_quats,
        detector_quats=np.array([[0.0, 0.0, 0.0, 1.0]]),
        bore2spin_quat=instr.bore2spin_quat,
    )

    colatitude = pointings_and_polangle[..., 0]
    longitude = pointings_and_polangle[..., 1]
    polangle = pointings_and_polangle[..., 2]

    assert np.allclose(colatitude, np.pi / 2), colatitude
    assert np.allclose(np.abs(polangle), np.pi / 2), polangle

    # The longitude should have changed by a fraction 23 hours /
    # 365.25 days of a complete circle (we have 24 samples, from t = 0
    # to t = 23 hr)
    assert np.allclose(np.abs(longitude[..., -1] - longitude[..., 0]),
                       2 * np.pi * 23 / 365.25 / 24)
Exemple #8
0
def test_scanning_quaternions(tmp_path):
    sim = lbs.Simulation(base_path=tmp_path / "simulation_dir",
                         start_time=0.0,
                         duration_s=61.0)
    fakedet = create_fake_detector(sampling_rate_hz=50.0)

    sim.create_observations(detectors=[fakedet],
                            num_of_obs_per_detector=1,
                            split_list_over_processes=False)
    assert len(sim.observations) == 1
    obs = sim.observations[0]

    sstr = lbs.SpinningScanningStrategy(spin_sun_angle_rad=0.0,
                                        precession_rate_hz=0.0,
                                        spin_rate_hz=1.0)
    sim.generate_spin2ecl_quaternions(scanning_strategy=sstr, delta_time_s=0.5)

    instr = lbs.InstrumentInfo(spin_boresight_angle_rad=np.deg2rad(15.0))
    detector_quat = np.array([[0.0, 0.0, 0.0, 1.0]])

    det2ecl_quats = lbs.get_det2ecl_quaternions(
        obs,
        spin2ecliptic_quats=sim.spin2ecliptic_quats,
        detector_quats=detector_quat,
        bore2spin_quat=instr.bore2spin_quat,
    )

    ecl2det_quats = lbs.get_ecl2det_quaternions(
        obs,
        spin2ecliptic_quats=sim.spin2ecliptic_quats,
        detector_quats=detector_quat,
        bore2spin_quat=instr.bore2spin_quat,
    )

    identity = np.array([0, 0, 0, 1])
    det2ecl_quats = det2ecl_quats.reshape(-1, 4)
    ecl2det_quats = ecl2det_quats.reshape(-1, 4)
    for i in range(det2ecl_quats.shape[0]):
        # Check that the two quaternions (ecl2det and det2ecl) are
        # actually one the inverse of the other
        quat = np.copy(det2ecl_quats[i, :])
        lbs.quat_right_multiply(quat, *ecl2det_quats[i, :])
        assert np.allclose(quat, identity)
Exemple #9
0
def test_simulation_pointings_spinning(tmp_path):
    sim = lbs.Simulation(base_path=tmp_path / "simulation_dir",
                         start_time=0.0,
                         duration_s=61.0)
    fakedet = create_fake_detector(sampling_rate_hz=50.0)

    sim.create_observations(detectors=[fakedet],
                            num_of_obs_per_detector=1,
                            split_list_over_processes=False)
    assert len(sim.observations) == 1
    obs = sim.observations[0]

    sstr = lbs.SpinningScanningStrategy(spin_sun_angle_rad=0.0,
                                        precession_rate_hz=0.0,
                                        spin_rate_hz=1.0)
    sim.generate_spin2ecl_quaternions(scanning_strategy=sstr, delta_time_s=0.5)

    instr = lbs.InstrumentInfo(spin_boresight_angle_rad=np.deg2rad(15.0))

    pointings_and_polangle = lbs.get_pointings(
        obs,
        spin2ecliptic_quats=sim.spin2ecliptic_quats,
        detector_quats=np.array([[0.0, 0.0, 0.0, 1.0]]),
        bore2spin_quat=instr.bore2spin_quat,
    )
    colatitude = pointings_and_polangle[..., 0]

    reference_spin2ecliptic_file = Path(
        __file__).parent / "reference_spin2ecl.txt.gz"
    reference = np.loadtxt(reference_spin2ecliptic_file)
    assert np.allclose(sim.spin2ecliptic_quats.quats, reference)

    reference_pointings_file = Path(
        __file__).parent / "reference_pointings.txt.gz"
    reference = np.loadtxt(reference_pointings_file)
    assert np.allclose(pointings_and_polangle[0, :, :], reference)

    # Check that the colatitude does not depart more than ±15° from
    # the Ecliptic
    assert np.allclose(np.rad2deg(np.max(colatitude)), 90 + 15, atol=0.01)
    assert np.allclose(np.rad2deg(np.min(colatitude)), 90 - 15, atol=0.01)

    sim.flush()
Exemple #10
0
import litebird_sim as lbs

sim = lbs.Simulation(base_path="/tmp/destriper_output",
                     start_time=0,
                     duration_s=86400.0)

sim.generate_spin2ecl_quaternions(
    scanning_strategy=lbs.SpinningScanningStrategy(
        spin_sun_angle_rad=np.deg2rad(30),  # CORE-specific parameter
        spin_rate_hz=0.5 / 60,  # Ditto
        # We use astropy to convert the period (4 days) in
        # seconds
        precession_rate_hz=1.0 / (4 * u.day).to("s").value,
    ))
instr = lbs.InstrumentInfo(name="core",
                           spin_boresight_angle_rad=np.deg2rad(65))

# We create two detectors, whose polarization angles are separated by π/2
sim.create_observations(
    detectors=[
        lbs.DetectorInfo(name="0A", sampling_rate_hz=10),
        lbs.DetectorInfo(name="0B",
                         sampling_rate_hz=10,
                         quat=lbs.quat_rotation_z(np.pi / 2)),
    ],
    dtype_tod=np.float64,
    n_blocks_time=lbs.MPI_COMM_WORLD.size,
    split_list_over_processes=False,
)

# Generate some white noise
Exemple #11
0
def test_solar_dipole_fit(tmpdir):
    tmpdir = Path(tmpdir)

    test = unittest.TestCase()

    # The purpose of this test is to simulate the motion of the spacecraft
    # for one year (see `time_span_s`) and produce *two* timelines: the first
    # is associated with variables `*_s_o` and refers to the case of a nonzero
    # velocity of the Solar System, and the second is associated with variables
    # `*_o` and assumes that the reference frame of the Solar System is the
    # same as the CMB's (so that there is no dipole).

    start_time = Time("2022-01-01")
    time_span_s = 365 * 24 * 3600
    nside = 256
    sampling_hz = 1

    sim = lbs.Simulation(start_time=start_time, duration_s=time_span_s)

    scanning = lbs.SpinningScanningStrategy(
        spin_sun_angle_rad=0.785_398_163_397_448_3,
        precession_rate_hz=8.664_850_513_998_931e-05,
        spin_rate_hz=0.000_833_333_333_333_333_4,
        start_time=start_time,
    )

    spin2ecliptic_quats = scanning.generate_spin2ecl_quaternions(
        start_time, time_span_s, delta_time_s=7200
    )

    instr = lbs.InstrumentInfo(
        boresight_rotangle_rad=0.0,
        spin_boresight_angle_rad=0.872_664_625_997_164_8,
        spin_rotangle_rad=3.141_592_653_589_793,
    )

    det = lbs.DetectorInfo(
        name="Boresight_detector",
        sampling_rate_hz=sampling_hz,
        bandcenter_ghz=100.0,
        quat=[0.0, 0.0, 0.0, 1.0],
    )

    (obs_s_o,) = sim.create_observations(detectors=[det])
    (obs_o,) = sim.create_observations(detectors=[det])

    pointings = lbs.scanning.get_pointings(
        obs_s_o,
        spin2ecliptic_quats=spin2ecliptic_quats,
        detector_quats=[det.quat],
        bore2spin_quat=instr.bore2spin_quat,
    )

    orbit_s_o = lbs.SpacecraftOrbit(obs_s_o.start_time)
    orbit_o = lbs.SpacecraftOrbit(obs_o.start_time, solar_velocity_km_s=0.0)

    assert orbit_s_o.solar_velocity_km_s == 369.8160
    assert orbit_o.solar_velocity_km_s == 0.0

    pos_vel_s_o = lbs.spacecraft_pos_and_vel(orbit_s_o, obs_s_o, delta_time_s=86400.0)
    pos_vel_o = lbs.spacecraft_pos_and_vel(orbit_o, obs_o, delta_time_s=86400.0)

    assert pos_vel_s_o.velocities_km_s.shape == (366, 3)
    assert pos_vel_o.velocities_km_s.shape == (366, 3)

    lbs.add_dipole_to_observations(
        obs_s_o, pointings, pos_vel_s_o, dipole_type=lbs.DipoleType.LINEAR
    )
    lbs.add_dipole_to_observations(
        obs_o, pointings, pos_vel_o, dipole_type=lbs.DipoleType.LINEAR
    )

    npix = hp.nside2npix(nside)
    pix_indexes = hp.ang2pix(nside, pointings[0, :, 0], pointings[0, :, 1])

    h = np.zeros(npix)
    m = np.zeros(npix)
    map_s_o = np.zeros(npix)
    map_o = np.zeros(npix)

    bin_map(
        tod=obs_s_o.tod,
        pixel_indexes=pix_indexes,
        binned_map=map_s_o,
        accum_map=m,
        hit_map=h,
    )
    import healpy

    healpy.write_map(tmpdir / "map_s_o.fits.gz", map_s_o, overwrite=True)

    bin_map(
        tod=obs_o.tod,
        pixel_indexes=pix_indexes,
        binned_map=map_o,
        accum_map=m,
        hit_map=h,
    )

    healpy.write_map(tmpdir / "map_o.fits.gz", map_o, overwrite=True)

    dip_map = map_s_o - map_o

    assert np.abs(np.nanmean(map_s_o) * 1e6) < 1
    assert np.abs(np.nanmean(map_o) * 1e6) < 1
    assert np.abs(np.nanmean(dip_map) * 1e6) < 1

    dip_map[np.isnan(dip_map)] = healpy.UNSEEN
    mono, dip = hp.fit_dipole(dip_map)

    r = hp.Rotator(coord=["E", "G"])
    l, b = hp.vec2ang(r(dip), lonlat=True)

    # Amplitude, longitude and latitude
    test.assertAlmostEqual(np.sqrt(np.sum(dip ** 2)) * 1e6, 3362.08, 1)
    test.assertAlmostEqual(l[0], 264.021, 1)
    test.assertAlmostEqual(b[0], 48.253, 1)
Exemple #12
0
def test_destriper(tmp_path):
    sim = lbs.Simulation(base_path=tmp_path / "destriper_output",
                         start_time=0,
                         duration_s=86400.0)

    sim.generate_spin2ecl_quaternions(
        scanning_strategy=lbs.SpinningScanningStrategy(
            spin_sun_angle_rad=np.deg2rad(30),  # CORE-specific parameter
            spin_rate_hz=0.5 / 60,  # Ditto
            # We use astropy to convert the period (4 days) in
            # seconds
            precession_rate_hz=1.0 / (4 * u.day).to("s").value,
        ))
    instr = lbs.InstrumentInfo(name="core",
                               spin_boresight_angle_rad=np.deg2rad(65))
    sim.create_observations(
        detectors=[
            lbs.DetectorInfo(name="0A", sampling_rate_hz=10),
            lbs.DetectorInfo(name="0B",
                             sampling_rate_hz=10,
                             quat=lbs.quat_rotation_z(np.pi / 2)),
        ],
        # num_of_obs_per_detector=lbs.MPI_COMM_WORLD.size,
        dtype_tod=np.float64,
        n_blocks_time=lbs.MPI_COMM_WORLD.size,
        split_list_over_processes=False,
    )

    # Generate some white noise
    rs = RandomState(MT19937(SeedSequence(123456789)))
    for curobs in sim.observations:
        curobs.tod *= 0.0
        curobs.tod += rs.randn(*curobs.tod.shape)

    params = lbs.DestriperParameters(
        nside=16,
        nnz=3,
        baseline_length=100,
        iter_max=10,
        return_hit_map=True,
        return_binned_map=True,
        return_destriped_map=True,
        return_npp=True,
        return_invnpp=True,
        return_rcond=True,
    )

    results = lbs.destripe(sim, instr, params=params)

    ref_map_path = Path(__file__).parent / "destriper_reference"

    hit_map_filename = ref_map_path / "destriper_hit_map.fits.gz"
    # healpy.write_map(hit_map_filename, results.hit_map, dtype="int32", overwrite=True)
    np.testing.assert_allclose(
        results.hit_map,
        healpy.read_map(hit_map_filename, field=None, dtype=np.int32))

    binned_map_filename = ref_map_path / "destriper_binned_map.fits.gz"
    # healpy.write_map(
    #     binned_map_filename,
    #     results.binned_map,
    #     dtype=list((np.float32 for i in range(3))),
    #     overwrite=True,
    # )
    ref_binned = healpy.read_map(binned_map_filename,
                                 field=None,
                                 dtype=list((np.float32 for i in range(3))))
    assert results.binned_map.shape == ref_binned.shape
    np.testing.assert_allclose(results.binned_map,
                               ref_binned,
                               rtol=1e-2,
                               atol=1e-3)

    destriped_map_filename = ref_map_path / "destriper_destriped_map.fits.gz"
    # healpy.write_map(
    #     destriped_map_filename,
    #     results.destriped_map,
    #     dtype=list((np.float32 for i in range(3))),
    #     overwrite=True,
    # )
    ref_destriped = healpy.read_map(destriped_map_filename,
                                    field=None,
                                    dtype=list((np.float32 for i in range(3))))
    assert results.destriped_map.shape == ref_destriped.shape
    np.testing.assert_allclose(results.destriped_map,
                               ref_destriped,
                               rtol=1e-2,
                               atol=1e-3)

    npp_filename = ref_map_path / "destriper_npp.fits.gz"
    # healpy.write_map(
    #     npp_filename,
    #     results.npp,
    #     dtype=list((np.float32 for i in range(6))),
    #     overwrite=True,
    # )
    ref_npp = healpy.read_map(npp_filename,
                              field=None,
                              dtype=list((np.float32 for i in range(6))))
    assert results.npp.shape == ref_npp.shape
    np.testing.assert_allclose(results.npp, ref_npp, rtol=1e-2, atol=1e-3)

    invnpp_filename = ref_map_path / "destriper_invnpp.fits.gz"
    # healpy.write_map(
    #     invnpp_filename,
    #     results.invnpp,
    #     dtype=list((np.float32 for i in range(6))),
    #     overwrite=True,
    # )
    ref_invnpp = healpy.read_map(invnpp_filename,
                                 field=None,
                                 dtype=list((np.float32 for i in range(6))))
    assert results.invnpp.shape == ref_invnpp.shape
    np.testing.assert_allclose(results.invnpp,
                               ref_invnpp,
                               rtol=1e-2,
                               atol=1e-3)

    rcond_filename = ref_map_path / "destriper_rcond.fits.gz"
    # healpy.write_map(
    #     rcond_filename,
    #     results.rcond,
    #     dtype=np.float32,
    #     overwrite=True,
    # )
    assert np.allclose(
        results.rcond,
        healpy.read_map(rcond_filename, field=None, dtype=np.float32))
Exemple #13
0
def test_scan_map():

    # The purpose of this test is to simulate the motion of the spacecraft
    # for one year (see `time_span_s`) and produce *two* maps: the first
    # is associated with the Observation `obs1` and is built using
    # `scan_map_in_observations` and `make_bin_map`, the second is associated
    # the Observation `obs2` and is built directly filling in the test
    # `tod`, `psi` and `pixind` and then using `make_bin_map`
    # In the final test `out_map1` is compared with both `out_map2` and the
    # input map. Both simulations use two orthogonal detectors at the boresight
    # and input maps generated with `np.random.normal`.

    start_time = 0
    time_span_s = 365 * 24 * 3600
    nside = 16
    sampling_hz = 1
    hwp_radpsec = 4.084_070_449_666_731

    npix = hp.nside2npix(nside)

    sim = lbs.Simulation(start_time=start_time, duration_s=time_span_s)

    scanning = lbs.SpinningScanningStrategy(
        spin_sun_angle_rad=0.785_398_163_397_448_3,
        precession_rate_hz=8.664_850_513_998_931e-05,
        spin_rate_hz=0.000_833_333_333_333_333_4,
        start_time=start_time,
    )

    spin2ecliptic_quats = scanning.generate_spin2ecl_quaternions(
        start_time, time_span_s, delta_time_s=7200)

    instr = lbs.InstrumentInfo(
        boresight_rotangle_rad=0.0,
        spin_boresight_angle_rad=0.872_664_625_997_164_8,
        spin_rotangle_rad=3.141_592_653_589_793,
    )

    detT = lbs.DetectorInfo(
        name="Boresight_detector_T",
        sampling_rate_hz=sampling_hz,
        bandcenter_ghz=100.0,
        quat=[0.0, 0.0, 0.0, 1.0],
    )

    detB = lbs.DetectorInfo(
        name="Boresight_detector_B",
        sampling_rate_hz=sampling_hz,
        bandcenter_ghz=100.0,
        quat=[0.0, 0.0, 1.0 / np.sqrt(2.0), 1.0 / np.sqrt(2.0)],
    )

    np.random.seed(seed=123_456_789)
    maps = np.random.normal(0, 1, (3, npix))

    in_map = {"Boresight_detector_T": maps, "Boresight_detector_B": maps}

    (obs1, ) = sim.create_observations(detectors=[detT, detB])
    (obs2, ) = sim.create_observations(detectors=[detT, detB])

    pointings = lbs.scanning.get_pointings(
        obs1,
        spin2ecliptic_quats=spin2ecliptic_quats,
        detector_quats=[detT.quat, detB.quat],
        bore2spin_quat=instr.bore2spin_quat,
    )

    lbs.scan_map_in_observations(obs1,
                                 pointings,
                                 hwp_radpsec,
                                 in_map,
                                 fill_psi_and_pixind_in_obs=True)
    out_map1 = lbs.make_bin_map(obs1, nside).T

    times = obs2.get_times()
    obs2.pixind = np.empty(obs2.tod.shape, dtype=np.int32)
    obs2.psi = np.empty(obs2.tod.shape)
    for idet in range(obs2.n_detectors):
        obs2.pixind[idet, :] = hp.ang2pix(nside, pointings[idet, :, 0],
                                          pointings[idet, :, 1])
        obs2.psi[idet, :] = np.mod(
            pointings[idet, :, 2] + 2 * times * hwp_radpsec, 2 * np.pi)

    for idet in range(obs2.n_detectors):
        obs2.tod[idet, :] = (
            maps[0, obs2.pixind[idet, :]] +
            np.cos(2 * obs2.psi[idet, :]) * maps[1, obs2.pixind[idet, :]] +
            np.sin(2 * obs2.psi[idet, :]) * maps[2, obs2.pixind[idet, :]])

    out_map2 = lbs.make_bin_map(obs2, nside).T

    np.testing.assert_allclose(out_map1,
                               in_map["Boresight_detector_T"],
                               rtol=1e-6,
                               atol=1e-6)

    np.testing.assert_allclose(out_map1, out_map2, rtol=1e-6, atol=1e-6)
Exemple #14
0
def main():
    warnings.filterwarnings("ignore", category=ErfaWarning)

    sim = lbs.Simulation(
        parameter_file=sys.argv[1],
        name="Observation of planets",
        description="""
This report contains the result of a simulation of the observation
of the sky, particularly with respect to the observation of planets.
""",
    )

    params = load_parameters(sim)

    if lbs.MPI_ENABLED:
        log.info("Using MPI with %d processes", lbs.MPI_COMM_WORLD.size)
    else:
        log.info("Not using MPI, serial execution")

    log.info("Generating the quaternions")
    scanning_strategy = read_scanning_strategy(
        sim.parameters["scanning_strategy"], sim.imo, sim.start_time)
    sim.generate_spin2ecl_quaternions(
        scanning_strategy=scanning_strategy,
        delta_time_s=params.spin2ecl_delta_time_s)

    log.info("Creating the observations")
    instr = lbs.InstrumentInfo(
        name="instrum",
        spin_boresight_angle_rad=params.spin_boresight_angle_rad)
    detector = lbs.DetectorInfo(
        sampling_rate_hz=params.detector_sampling_rate_hz)

    conversions = [
        ("years", astropy.units.year),
        ("year", astropy.units.year),
        ("days", astropy.units.day),
        ("day", astropy.units.day),
        ("hours", astropy.units.hour),
        ("hour", astropy.units.hour),
        ("minutes", astropy.units.minute),
        ("min", astropy.units.minute),
        ("sec", astropy.units.second),
        ("s", astropy.units.second),
        ("km", astropy.units.kilometer),
        ("Km", astropy.units.kilometer),
        ("au", astropy.units.au),
        ("AU", astropy.units.au),
        ("deg", astropy.units.deg),
        ("rad", astropy.units.rad),
    ]

    def conversion(x, new_unit):
        if isinstance(x, str):
            for conv_str, conv_unit in conversions:
                if x.endswith(" " + conv_str):
                    value = float(x.replace(conv_str, ""))
                    return (value * conv_unit).to(new_unit).value
                    break
        else:
            return float(x)

    sim_params = sim.parameters["simulation"]
    durations = ["duration_s", "duration_of_obs_s"]
    for dur in durations:
        sim_params[dur] = conversion(sim_params[dur], "s")

    delta_t_s = sim_params["duration_of_obs_s"]
    sim.create_observations(
        detectors=[detector],
        num_of_obs_per_detector=int(sim_params["duration_s"] /
                                    sim_params["duration_of_obs_s"]),
    )

    #################################################################
    # Here begins the juicy part

    log.info("The loop starts on %d processes", lbs.MPI_COMM_WORLD.size)
    sky_hitmap = np.zeros(healpy.nside2npix(params.output_nside),
                          dtype=np.int32)
    detector_hitmap = np.zeros(healpy.nside2npix(params.output_nside),
                               dtype=np.int32)
    dist_map_m2 = np.zeros(len(detector_hitmap))

    iterator = tqdm
    if lbs.MPI_ENABLED and lbs.MPI_COMM_WORLD.rank != 0:
        iterator = lambda x: x

    # Time variable inizialized at the beginning of the simulation
    t = 0.0
    for obs in iterator(sim.observations):
        solar_system_ephemeris.set("builtin")

        times = obs.get_times(astropy_times=True)

        # We only compute the planet's position for the first sample in
        # the observation and then assume that it does not move
        # significantly. (In Ecliptic coordinates, Jupiter moves by
        # fractions of an arcmin over a time span of one hour)
        time0 = times[0]
        icrs_pos = get_ecliptic_vec(
            get_body_barycentric(params.planet_name, time0))
        earth_pos = get_ecliptic_vec(get_body_barycentric("earth", time0))

        # Move the spacecraft to L2
        L2_pos = earth_pos * (
            1.0 + params.earth_L2_distance_km / norm(earth_pos).to("km").value)
        # Creating a Lissajous orbit
        R1 = conversion(params.radius_au[0], "au")
        R2 = conversion(params.radius_au[1], "au")
        phi1_t = params.L2_orbital_velocity_rad_s[0] * t
        phi2_t = params.L2_orbital_velocity_rad_s[1] * t
        phase = conversion(params.phase_rad, "rad")
        orbit_pos = np.array([
            -R1 * np.sin(np.arctan(L2_pos[1] / L2_pos[0])) * np.cos(phi1_t),
            R1 * np.cos(np.arctan(L2_pos[1] / L2_pos[0])) * np.cos(phi1_t),
            R2 * np.sin(phi2_t + phase),
        ])
        orbit_pos = astropy.units.Quantity(orbit_pos, unit="AU")

        # Move the spacecraft to a Lissajous orbit around L2
        sat_pos = orbit_pos + L2_pos

        # Compute the distance between the spacecraft and the planet
        distance_m = norm(sat_pos - icrs_pos).to("m").value

        # This is the direction of the solar system body with respect
        # to the spacecraft, in Ecliptic coordinates
        ecl_vec = (icrs_pos - sat_pos).value

        # The variable ecl_vec is a 3-element vector. We normalize it so
        # that it has length one (using the L_2 norm, hence ord=2)
        ecl_vec /= np.linalg.norm(ecl_vec, axis=0, ord=2)

        # Convert the matrix to a N×3 shape by repeating the vector:
        # planets move slowly, so we assume that the planet stays fixed
        # during this observation.
        ecl_vec = np.repeat(ecl_vec.reshape(1, 3), len(times), axis=0)

        # Calculate the quaternions that convert the Ecliptic
        # reference system into the detector's reference system
        quats = lbs.get_ecl2det_quaternions(
            obs,
            sim.spin2ecliptic_quats,
            detector_quats=[detector.quat],
            bore2spin_quat=instr.bore2spin_quat,
        )

        # Make room for the xyz vectors in the detector's reference frame
        det_vec = np.empty_like(ecl_vec)

        # Do the rotation!
        lbs.all_rotate_vectors(det_vec, quats[0], ecl_vec)

        pixidx = healpy.vec2pix(params.output_nside, det_vec[:, 0],
                                det_vec[:, 1], det_vec[:, 2])
        bincount = np.bincount(pixidx, minlength=len(detector_hitmap))
        detector_hitmap += bincount
        dist_map_m2 += bincount / ((4 * np.pi * (distance_m**2))**2)

        pointings = lbs.get_pointings(obs, sim.spin2ecliptic_quats,
                                      [detector.quat], instr.bore2spin_quat)[0]

        pixidx = healpy.ang2pix(params.output_nside, pointings[:, 0],
                                pointings[:, 1])
        bincount = np.bincount(pixidx, minlength=len(sky_hitmap))
        sky_hitmap += bincount

        # updating the time variable
        t += delta_t_s

    if lbs.MPI_ENABLED:
        sky_hitmap = lbs.MPI_COMM_WORLD.allreduce(sky_hitmap)
        detector_hitmap = lbs.MPI_COMM_WORLD.allreduce(detector_hitmap)
        dist_map_m2 = lbs.MPI_COMM_WORLD.allreduce(dist_map_m2)

    time_map_s = detector_hitmap / params.detector_sampling_rate_hz
    dist_map_m2[dist_map_m2 > 0] = np.power(dist_map_m2[dist_map_m2 > 0], -0.5)

    obs_time_per_radius_s = [
        time_per_radius(time_map_s, angular_radius_rad=np.deg2rad(radius_deg))
        for radius_deg in params.radii_deg
    ]

    if lbs.MPI_COMM_WORLD.rank == 0:
        # Create a plot of the observation time of the planet as a
        # function of the angular radius
        fig, ax = plt.subplots()
        ax.loglog(params.radii_deg, obs_time_per_radius_s)
        ax.set_xlabel("Radius [deg]")
        ax.set_ylabel("Observation time [s]")

        # Create a map showing how the observation time is distributed on
        # the sphere (in the reference frame of the detector)
        healpy.orthview(time_map_s,
                        title="Time spent observing the source",
                        unit="s")

        if scanning_strategy.spin_rate_hz != 0:
            spin_period_min = 1.0 / (60.0 * scanning_strategy.spin_rate_hz)
        else:
            spin_period_min = 0.0

        if scanning_strategy.precession_rate_hz != 0:
            precession_period_min = 1.0 / (
                60.0 * scanning_strategy.precession_rate_hz)
        else:
            precession_period_min = 0.0

        sim.append_to_report(
            """

## Scanning strategy parameters

Parameter | Value
--------- | --------------
Angle between the spin axis and the Sun-Earth axis | {{ sun_earth_angle_deg }} deg
Angle between the spin axis and the boresight | {{ spin_boresight_angle_deg }} deg
Precession period | {{ precession_period_min }} min
Spin period | {{ spin_period_min }} min

## Observation of {{ params.planet_name | capitalize }}

![](detector_hitmap.png)

The overall time spent in the map is {{ overall_time_s }} seconds.

The time resolution of the simulation was {{ delta_time_s }} seconds.

Angular radius [deg] | Time spent [s]
-------------------- | ------------------------
{% for row in radius_vs_time_s -%}
{{ "%.1f"|format(row[0]) }} | {{ "%.1f"|format(row[1]) }}
{% endfor -%}

![](radius_vs_time.svg)

""",
            figures=[(plt.gcf(), "detector_hitmap.png"),
                     (fig, "radius_vs_time.svg")],
            params=params,
            overall_time_s=np.sum(detector_hitmap) /
            params.detector_sampling_rate_hz,
            radius_vs_time_s=list(zip(params.radii_deg,
                                      obs_time_per_radius_s)),
            delta_time_s=1.0 / params.detector_sampling_rate_hz,
            sun_earth_angle_deg=np.rad2deg(
                scanning_strategy.spin_sun_angle_rad),
            spin_boresight_angle_deg=np.rad2deg(
                params.spin_boresight_angle_rad),
            precession_period_min=precession_period_min,
            spin_period_min=spin_period_min,
            det=detector,
        )

        healpy.write_map(
            params.output_map_file_name,
            (detector_hitmap, time_map_s, dist_map_m2, sky_hitmap),
            coord="DETECTOR",
            column_names=["HITS", "OBSTIME", "SQDIST", "SKYHITS"],
            column_units=["", "s", "m^2", ""],
            dtype=[np.int32, np.float32, np.float64, np.int32],
            overwrite=True,
        )

        np.savetxt(
            params.output_table_file_name,
            np.array(list(zip(params.radii_deg, obs_time_per_radius_s))),
            fmt=["%.2f", "%.5e"],
        )

        with (sim.base_path / "parameters.json").open("wt") as outf:
            time_value = scanning_strategy.start_time
            if not isinstance(time_value, (int, float)):
                time_value = str(time_value)
            json.dump(
                {
                    "scanning_strategy": {
                        "spin_sun_angle_rad":
                        scanning_strategy.spin_sun_angle_rad,
                        "precession_rate_hz":
                        scanning_strategy.precession_rate_hz,
                        "spin_rate_hz": scanning_strategy.spin_rate_hz,
                        "start_time": time_value,
                    },
                    "detector": {
                        "sampling_rate_hz": params.detector_sampling_rate_hz
                    },
                    "planet": {
                        "name": params.planet_name
                    },
                },
                outf,
                indent=2,
            )

    sim.flush()
Exemple #15
0
    precession_rate_hz=0,
    spin_rate_hz=1 / 60,
    start_time=start_time,
)

# The spacecraft spins pretty fast (once per minute!), so we
# need to pick delta_time_s ≪ 1/spin_rate_hz
spin2ecliptic_quats = scanning.generate_spin2ecl_quaternions(start_time,
                                                             time_span_s,
                                                             delta_time_s=5.0)

# We simulate an instrument whose boresight is perpendicular to
# the spin axis.
instr = lbs.InstrumentInfo(
    boresight_rotangle_rad=0.0,
    spin_boresight_angle_rad=np.deg2rad(90),
    spin_rotangle_rad=np.deg2rad(75),
)

# A simple detector looking along the boresight direction
det = lbs.DetectorInfo(
    name="Boresight_detector",
    sampling_rate_hz=sampling_hz,
    bandcenter_ghz=100.0,
)

(obs, ) = sim.create_observations(detectors=[det])
pointings = lbs.scanning.get_pointings(
    obs,
    spin2ecliptic_quats=spin2ecliptic_quats,
    detector_quats=[det.quat],