diff --git a/src/toast/intervals.py b/src/toast/intervals.py index 0f199193b..b475e2585 100644 --- a/src/toast/intervals.py +++ b/src/toast/intervals.py @@ -94,8 +94,16 @@ def __init__(self, timestamps, intervals=None, timespans=None, samplespans=None) if np.isclose(timespans[i][1], timespans[i + 1][0], rtol=1e-12): # Force nearly equal timestamps to match timespans[i][1] = timespans[i + 1][0] + # Check that the intervals are sorted and disjoint if timespans[i][1] > timespans[i + 1][0]: - raise RuntimeError("Timespans must be sorted and disjoint") + t1 = timespans[i][1] + t2 = timespans[i + 1][0] + dt = t1 - t2 + ts = np.median(np.diff(timestamps)) + msg = f"Timespans must be sorted and disjoint" + msg += f" but {t1} - {t2} = {dt} s = {dt / ts} samples)" + raise RuntimeError(msg) + # Map interval times into sample indices indices, times = self._find_indices(timespans) self.data = np.array( [ @@ -186,7 +194,12 @@ def __len__(self): return len(self.data) def __repr__(self): - return self.data.__repr__() + s = " np.abs(x[i] + period - x[i - 1]): + x[i] += period + y[i] += period + while np.abs(x[i] - x[i - 1]) > np.abs(x[i] - period - x[i - 1]): + x[i] -= period + y[i] -= period + return + + @trait_docs class PixelsWCS(Operator): """Operator which generates detector pixel indices defined on a flat projection. @@ -439,26 +451,29 @@ def _exec(self, data, detectors=None, **kwargs): ) minmax = minmax.T # Compact observations on both sides of the zero meridian - # can confuse this calculation. Use np.unwrap() to find - # the most compact longitude range. - lonmin = np.amin(np.unwrap(minmax[0])) - lonmax = np.amax(np.unwrap(minmax[1])) - if lonmax < lonmin: - lonmax += 2 * np.pi + # can confuse this calculation. We must unwrap the per-observation + # limits for the most compact longitude range. + unwrap_together(minmax[0], minmax[1]) + lonmin = np.amin(minmax[0]) + lonmax = np.amin(minmax[1]) latmin = np.amin(minmax[2]) latmax = np.amax(minmax[3]) if data.comm.comm_world is not None: # Zero meridian concern applies across processes - all_lonmin = data.comm.comm_world.allgather(lonmin.to_value(u.radian)) - all_lonmax = data.comm.comm_world.allgather(lonmax.to_value(u.radian)) - all_latmin = data.comm.comm_world.allgather(latmin.to_value(u.radian)) - all_latmax = data.comm.comm_world.allgather(latmax.to_value(u.radian)) - lonmin = np.amin(np.unwrap(all_lonmin)) * u.radian - lonmax = np.amax(np.unwrap(all_lonmax)) * u.radian - if lonmax < lonmin: - lonmax += 2 * np.pi - latmin = np.amin(all_latmin) * u.radian - latmax = np.amax(all_latmax) * u.radian + def gather(x): + return ( + data.comm.comm_world.allgather(x.to_value(u.radian)) * u.radian + ) + + all_lonmin = gather(lonmin) + all_lonmax = gather(lonmax) + all_latmin = gather(latmin) + all_latmax = gather(latmax) + unwrap_together(all_lonmin, all_lonmax) + lonmin = np.amin(all_lonmin) + lonmax = np.amin(all_lonmax) + latmin = np.amin(all_latmin) + latmax = np.amax(all_latmax) self.bounds = ( lonmin.to(u.degree), lonmax.to(u.degree), diff --git a/src/toast/spt3g/spt3g_export.py b/src/toast/spt3g/spt3g_export.py index b24fdee9f..1ececb29b 100644 --- a/src/toast/spt3g/spt3g_export.py +++ b/src/toast/spt3g/spt3g_export.py @@ -10,7 +10,7 @@ from astropy import units as u from ..instrument import GroundSite, SpaceSite -from ..intervals import IntervalList +from ..intervals import IntervalList, interval_dtype from ..timing import function_timer from ..utils import Environment, Logger, object_fullname from .spt3g_utils import ( @@ -167,7 +167,20 @@ def export_intervals(obs, name, iframe): (G3Object): The best container available. """ - overlap = iframe & obs.intervals[name] + overlap = [] + frame = iframe.data[0] + for ival in obs.intervals[name]: + if frame.start <= ival.stop and frame.stop >= ival.start: + overlap.append(( + ival.start, + ival.stop, + max(frame.first, ival.first), + min(frame.last, ival.last), + )) + overlap = IntervalList( + iframe.timestamps, + intervals=np.array(overlap, dtype=interval_dtype).view(np.recarray), + ) out = None try: diff --git a/src/toast/tests/helpers/ground.py b/src/toast/tests/helpers/ground.py index 209029889..6e42e06ed 100644 --- a/src/toast/tests/helpers/ground.py +++ b/src/toast/tests/helpers/ground.py @@ -93,6 +93,7 @@ def create_ground_data( turnarounds_invalid=False, single_group=False, flagged_pixels=True, + schedule_hours=2, ): """Create a data object with a simple ground sim. @@ -137,6 +138,7 @@ def create_ground_data( if tdir is None: tdir = tempfile.mkdtemp() + sch_hours = f"{int(schedule_hours):02d}" sch_file = os.path.join(tdir, "ground_schedule.txt") run_scheduler( opts=[ @@ -155,7 +157,7 @@ def create_ground_data( "--start", "2020-01-01 00:00:00", "--stop", - "2020-01-01 06:00:00", + f"2020-01-01 {sch_hours}:00:00", "--out", sch_file, ] @@ -218,6 +220,7 @@ def create_overdistributed_data( freqs=None, turnarounds_invalid=False, single_group=False, + schedule_hours=2, ): """Create a data object with more detectors than processes. @@ -264,6 +267,7 @@ def create_overdistributed_data( if tdir is None: tdir = tempfile.mkdtemp() + sch_hours = f"{int(schedule_hours):02d}" sch_file = os.path.join(tdir, "ground_schedule.txt") run_scheduler( opts=[ @@ -282,7 +286,7 @@ def create_overdistributed_data( "--start", "2020-01-01 00:00:00", "--stop", - "2020-01-01 06:00:00", + f"2020-01-01 {sch_hours}:00:00", "--out", sch_file, ] diff --git a/src/toast/tests/ops_hwpss_model.py b/src/toast/tests/ops_hwpss_model.py index f811fae88..5d6bc69c1 100644 --- a/src/toast/tests/ops_hwpss_model.py +++ b/src/toast/tests/ops_hwpss_model.py @@ -106,7 +106,7 @@ def create_test_data(self, testdir): weights, skyfile, "input_sky_dist", - map_key="input_sky", + map_key=map_key, fwhm=30.0 * u.arcmin, lmax=3 * pixels.nside, I_scale=0.001, @@ -274,7 +274,7 @@ def test_hwpss_basic(self): os.makedirs(testdir) data, tod_rms, coeff = self.create_test_data(testdir) - n_harmonics = len(coeff) // 4 + n_harmonics = len(coeff[data.obs[0].name]) // 4 # Add random DC level for ob in data.obs: @@ -331,7 +331,7 @@ def test_hwpss_relcal_fixed(self): os.makedirs(testdir) data, tod_rms, coeff = self.create_test_data(testdir) - n_harmonics = len(coeff) // 4 + n_harmonics = len(coeff[data.obs[0].name]) // 4 # Apply a random inverse relative calibration np.random.seed(123456) @@ -400,7 +400,7 @@ def test_hwpss_relcal_continuous(self): os.makedirs(testdir) data, tod_rms, coeff = self.create_test_data(testdir) - n_harmonics = len(coeff) // 4 + n_harmonics = len(coeff[data.obs[0].name]) // 4 # Apply a random inverse relative calibration that is time-varying np.random.seed(123456)