From 3b00b61fd5e1bfa72ffd69b419a911ed715489f1 Mon Sep 17 00:00:00 2001 From: Aashrita Mangu Date: Thu, 2 Jul 2026 16:51:44 -0400 Subject: [PATCH 1/6] BUG FIX: Updated get_turnaround_flags so that it can catch stationary observations that may trickle through --- sotodlib/tod_ops/flags.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sotodlib/tod_ops/flags.py b/sotodlib/tod_ops/flags.py index 6f6b36b8a..17915be4a 100644 --- a/sotodlib/tod_ops/flags.py +++ b/sotodlib/tod_ops/flags.py @@ -195,6 +195,9 @@ def get_turnaround_flags(aman, az=None, method='scanspeed', name='turnarounds', (Optional). Also merge an AxisManager with subscan information. turnarounds_in_subscan : bool (Optional). Turnarounds are included as part of a subscan. + az_throw_threshold : float + Minimum azimuth throw required to attempt turnaround flagging. If ``aman.obs_info.az_throw`` falls below this value, + the obs is treated as stationary or incomplete and a ``ValueError`` is raised instead. Returns ------- @@ -203,7 +206,14 @@ def get_turnaround_flags(aman, az=None, method='scanspeed', name='turnarounds', """ if az is None : az = aman.boresight.az - + + # Check that telescope was scanning. + if aman.obs_info.az_throw < az_throw_threshold: + raise ValueError( + f"Azimuth motion {aman.obs_info.az_throw} is too small compared to threshold {az_throw_threshold} + to compute turnaround flags" + ) + if method not in ['az', 'scanspeed']: raise ValueError('Unsupported method. Supported methods are `az` or `scanspeed`') From 6f6044d339e87a94ef1993ce4f4026e39f781547 Mon Sep 17 00:00:00 2001 From: Aashrita Mangu Date: Thu, 2 Jul 2026 16:58:27 -0400 Subject: [PATCH 2/6] Forgot to include it in the function call :] --- sotodlib/tod_ops/flags.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sotodlib/tod_ops/flags.py b/sotodlib/tod_ops/flags.py index 17915be4a..41a4f50d4 100644 --- a/sotodlib/tod_ops/flags.py +++ b/sotodlib/tod_ops/flags.py @@ -157,7 +157,8 @@ def get_det_bias_flags(aman, detcal=None, rfrac_range=(0.1, 0.7), def get_turnaround_flags(aman, az=None, method='scanspeed', name='turnarounds', merge=True, merge_lr=True, overwrite=True, t_buffer=2., kernel_size=400, peak_threshold=0.1, rel_distance_peaks=0.3, - truncate=False, qlim=1, merge_subscans=True, turnarounds_in_subscan=False): + truncate=False, qlim=1, merge_subscans=True, turnarounds_in_subscan=False, + az_throw_threshold=None): """ Compute turnaround flags for a dataset. From 4c5dada913d19df1fa2448ab647a5c30055bd802 Mon Sep 17 00:00:00 2001 From: Aashrita Mangu Date: Thu, 2 Jul 2026 17:30:20 -0400 Subject: [PATCH 3/6] Fixed some syntax errors and tested that it ran ok --- sotodlib/tod_ops/flags.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sotodlib/tod_ops/flags.py b/sotodlib/tod_ops/flags.py index 41a4f50d4..f0323bc2d 100644 --- a/sotodlib/tod_ops/flags.py +++ b/sotodlib/tod_ops/flags.py @@ -205,14 +205,19 @@ def get_turnaround_flags(aman, az=None, method='scanspeed', name='turnarounds', Ranges : RangesMatrix The turnaround flags as a Ranges object. """ + if az_throw_threshold is None: + raise ValueError( + "`az_throw_threshold` is required and cannot be None" + ) + if az is None : az = aman.boresight.az # Check that telescope was scanning. if aman.obs_info.az_throw < az_throw_threshold: raise ValueError( - f"Azimuth motion {aman.obs_info.az_throw} is too small compared to threshold {az_throw_threshold} - to compute turnaround flags" + f"""Azimuth motion {aman.obs_info.az_throw} is too small compared to threshold {az_throw_threshold} + to compute turnaround flags""" ) if method not in ['az', 'scanspeed']: From 902bd031ef640a9dad42be50cde52afa3f110eee Mon Sep 17 00:00:00 2001 From: Aashrita Mangu Date: Thu, 2 Jul 2026 17:46:31 -0400 Subject: [PATCH 4/6] Added an arbitrary value in test_psd.py for the az_throw_threshold. Added 0. for the az_throw_threshold for sub_polyf.py since it doesn't need a real threshold if being called. --- sotodlib/tod_ops/sub_polyf.py | 4 ++-- tests/test_psd.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sotodlib/tod_ops/sub_polyf.py b/sotodlib/tod_ops/sub_polyf.py index 66aeec36c..4d4cfda27 100644 --- a/sotodlib/tod_ops/sub_polyf.py +++ b/sotodlib/tod_ops/sub_polyf.py @@ -48,7 +48,7 @@ def subscan_polyfilter(aman, degree, signal_name="signal", exclude_turnarounds=F if exclude_turnarounds: if ("left_scan" not in aman.flags) or ("turnarounds" not in aman.flags): logger.warning('aman does not have left/right scan or turnarounds flag. `sotodlib.flags.get_turnaround_flags` will be ran with default parameters') - _ = flags.get_turnaround_flags(aman,truncate=True) + _ = flags.get_turnaround_flags(aman,az_throw_threshold=0.,truncate=True) ls_mask = aman.flags["left_scan"].mask() rs_mask = aman.flags["right_scan"].mask() ta_mask = aman.flags["turnarounds"].mask() @@ -60,7 +60,7 @@ def subscan_polyfilter(aman, degree, signal_name="signal", exclude_turnarounds=F else: if ("left_scan" not in aman.flags): logger.warning('aman does not have left/right scan. `sotodlib.flags.get_turnaround_flags` will be ran with default parameters') - _ = flags.get_turnaround_flags(aman,truncate=True) + _ = flags.get_turnaround_flags(aman,az_throw_threshold=0.,truncate=True) ls_mask = aman.flags["left_scan"].mask() rs_mask = aman.flags["right_scan"].mask() diff --git a/tests/test_psd.py b/tests/test_psd.py index 0cfe9a34c..fdcd8b4f3 100644 --- a/tests/test_psd.py +++ b/tests/test_psd.py @@ -84,7 +84,7 @@ def test_wn_debias(self): boresight.wrap("az", az, [(0, "samps")]) aman.wrap('boresight', boresight) aman.wrap('flags', core.AxisManager(aman.dets, aman.samps)) - get_turnaround_flags(aman) + get_turnaround_flags(aman, az_throw_threshold = 0.1) # test default arguments, this is biased calc_psd(aman, merge=True, nperseg=2**18) From ba00f785e8275ac5081524971ebba4310becc649 Mon Sep 17 00:00:00 2001 From: Aashrita Mangu Date: Wed, 15 Jul 2026 01:21:37 -0400 Subject: [PATCH 5/6] Now select cuts empty ta flags, which are set in the flagging itself if the throw is below threshold --- sotodlib/preprocess/processes.py | 16 +++++++++++++++- sotodlib/tod_ops/flags.py | 29 ++++++++++++++--------------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/sotodlib/preprocess/processes.py b/sotodlib/preprocess/processes.py index 649d99b0f..94353c58b 100644 --- a/sotodlib/preprocess/processes.py +++ b/sotodlib/preprocess/processes.py @@ -1510,7 +1510,8 @@ def calc_and_save(self, aman, proc_aman): calc_aman.wrap('turnarounds', ta, [(0, 'dets'), (1, 'samps')]) if ('merge_subscans' not in self.calc_cfgs) or (self.calc_cfgs['merge_subscans']): - calc_aman.wrap('subscan_info', aman.subscan_info) + if hasattr(aman, 'subscan_info'): + calc_aman.wrap('subscan_info', aman.subscan_info) self.save(proc_aman, calc_aman) return aman, proc_aman @@ -1526,6 +1527,19 @@ def process(self, aman, proc_aman, sim=False, data_aman=None): raise NotImplementedError("No support for using data AxisManager in process") tod_ops.flags.get_turnaround_flags(aman, **self.process_cfgs) return aman, proc_aman + + def select(self, meta, proc_aman=None, in_place=True): + if self.select_cfgs is None: + return meta + if proc_aman is None: + proc_aman = meta.preprocess + + ta = proc_aman[self.save_name].turnarounds + + if np.all(count_cuts(ta) == 0): + meta.restrict('dets', meta.dets.vals[:0]) + + return meta class SubPolyf(_Preprocess): """Fit TOD in each subscan with polynominal of given order and subtract it. diff --git a/sotodlib/tod_ops/flags.py b/sotodlib/tod_ops/flags.py index f0323bc2d..e8689be3f 100644 --- a/sotodlib/tod_ops/flags.py +++ b/sotodlib/tod_ops/flags.py @@ -158,7 +158,7 @@ def get_turnaround_flags(aman, az=None, method='scanspeed', name='turnarounds', merge=True, merge_lr=True, overwrite=True, t_buffer=2., kernel_size=400, peak_threshold=0.1, rel_distance_peaks=0.3, truncate=False, qlim=1, merge_subscans=True, turnarounds_in_subscan=False, - az_throw_threshold=None): + az_throw_threshold=0.): """ Compute turnaround flags for a dataset. @@ -197,29 +197,28 @@ def get_turnaround_flags(aman, az=None, method='scanspeed', name='turnarounds', turnarounds_in_subscan : bool (Optional). Turnarounds are included as part of a subscan. az_throw_threshold : float - Minimum azimuth throw required to attempt turnaround flagging. If ``aman.obs_info.az_throw`` falls below this value, - the obs is treated as stationary or incomplete and a ``ValueError`` is raised instead. + (Optional). Minimum azimuth throw required to attempt turnaround flagging. Returns no turnarounds if below threshold. Returns ------- Ranges : RangesMatrix The turnaround flags as a Ranges object. """ - if az_throw_threshold is None: - raise ValueError( - "`az_throw_threshold` is required and cannot be None" - ) - if az is None : az = aman.boresight.az - # Check that telescope was scanning. - if aman.obs_info.az_throw < az_throw_threshold: - raise ValueError( - f"""Azimuth motion {aman.obs_info.az_throw} is too small compared to threshold {az_throw_threshold} - to compute turnaround flags""" - ) + # Get the throw (convert to degrees). + az_throw = np.ptp(az) * 90 / np.pi + # If the throw is below the threshold, just output empty flags + if az_throw < az_throw_threshold: + ta_flag = Ranges.from_bitmask(np.zeros(aman.samps.count, dtype=bool)) + ta_exp = RangesMatrix([ta_flag for i in range(aman.dets.count)]) + if method == 'scanspeed': + return ta_exp, ta_exp, ta_exp + return ta_exp + + if method not in ['az', 'scanspeed']: raise ValueError('Unsupported method. Supported methods are `az` or `scanspeed`') @@ -1253,4 +1252,4 @@ def get_good_distribution_flags(aman, param_name='wn_signal', det_mask[aman[param_name] >= np.nanmax(distributions)] = False else: det_mask[aman[param_name] <= np.nanmin(distributions)] = False - return det_mask + return det_mask \ No newline at end of file From f9a9e38a275ce273d6e05d068fdfafd43345b9c6 Mon Sep 17 00:00:00 2001 From: Aashrita Mangu Date: Wed, 15 Jul 2026 01:25:39 -0400 Subject: [PATCH 6/6] Forgot to remove the old changes in the tests --- sotodlib/tod_ops/sub_polyf.py | 4 ++-- tests/test_psd.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sotodlib/tod_ops/sub_polyf.py b/sotodlib/tod_ops/sub_polyf.py index 4d4cfda27..66aeec36c 100644 --- a/sotodlib/tod_ops/sub_polyf.py +++ b/sotodlib/tod_ops/sub_polyf.py @@ -48,7 +48,7 @@ def subscan_polyfilter(aman, degree, signal_name="signal", exclude_turnarounds=F if exclude_turnarounds: if ("left_scan" not in aman.flags) or ("turnarounds" not in aman.flags): logger.warning('aman does not have left/right scan or turnarounds flag. `sotodlib.flags.get_turnaround_flags` will be ran with default parameters') - _ = flags.get_turnaround_flags(aman,az_throw_threshold=0.,truncate=True) + _ = flags.get_turnaround_flags(aman,truncate=True) ls_mask = aman.flags["left_scan"].mask() rs_mask = aman.flags["right_scan"].mask() ta_mask = aman.flags["turnarounds"].mask() @@ -60,7 +60,7 @@ def subscan_polyfilter(aman, degree, signal_name="signal", exclude_turnarounds=F else: if ("left_scan" not in aman.flags): logger.warning('aman does not have left/right scan. `sotodlib.flags.get_turnaround_flags` will be ran with default parameters') - _ = flags.get_turnaround_flags(aman,az_throw_threshold=0.,truncate=True) + _ = flags.get_turnaround_flags(aman,truncate=True) ls_mask = aman.flags["left_scan"].mask() rs_mask = aman.flags["right_scan"].mask() diff --git a/tests/test_psd.py b/tests/test_psd.py index fdcd8b4f3..0cfe9a34c 100644 --- a/tests/test_psd.py +++ b/tests/test_psd.py @@ -84,7 +84,7 @@ def test_wn_debias(self): boresight.wrap("az", az, [(0, "samps")]) aman.wrap('boresight', boresight) aman.wrap('flags', core.AxisManager(aman.dets, aman.samps)) - get_turnaround_flags(aman, az_throw_threshold = 0.1) + get_turnaround_flags(aman) # test default arguments, this is biased calc_psd(aman, merge=True, nperseg=2**18)