From b43e1cb63803ad433e5c379dd2325f39ce0e1f76 Mon Sep 17 00:00:00 2001 From: ykyohei <38639108+ykyohei@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:30:31 -0400 Subject: [PATCH 1/3] change default apodization --- sotodlib/hwp/hwp.py | 62 ++++++++++++++++---------------- sotodlib/preprocess/processes.py | 1 + sotodlib/tod_ops/apodize.py | 25 +++++++++---- sotodlib/tod_ops/azss.py | 48 +++++++++---------------- sotodlib/tod_ops/t2pleakage.py | 2 +- 5 files changed, 68 insertions(+), 70 deletions(-) diff --git a/sotodlib/hwp/hwp.py b/sotodlib/hwp/hwp.py index 522057fcb..1ce47f556 100644 --- a/sotodlib/hwp/hwp.py +++ b/sotodlib/hwp/hwp.py @@ -12,7 +12,7 @@ def get_hwpss(aman, signal=None, hwp_angle=None, bin_signal=True, bins=360, lin_reg=True, modes=[1, 2, 3, 4, 5, 6, 7, 8], apply_prefilt=True, prefilt_cfg=None, prefilt_detrend='linear', flags=None, apodize_edges=True, apodize_edges_samps=1600, - apodize_flags=True, apodize_flags_samps=200, + apodize_flags=True, apodize_flags_samps=200, apo_type='C1', merge_stats=True, hwpss_stats_name='hwpss_stats', merge_model=True, hwpss_model_name='hwpss_model'): r""" @@ -52,6 +52,16 @@ def get_hwpss(aman, signal=None, hwp_angle=None, bin_signal=True, bins=360, flags : str or RangesMatrix or Ranges, optional Flags to be masked out before extracting HWPSS. If Default is None, and no mask will be applied. If provided by a string, `aman.flags.get(flags)` is used for the flags. + apodize_edges : bool, optional + If True, applies an apodization window to the edges of the signal. Defaults to True. + apodize_edges_samps : int, optional + The number of samples over which to apply the edge apodization window. Defaults to 1600. + apodize_flags : bool, optional + If True, applies an apodization window based on the flags. Defaults to True. + apodize_flags_samps : int, optional + The number of samples over which to apply the flags apodization window. Defaults to 200. + apo_type: str, optional + Type of apodization, default is C1. merge_stats : bool, optional Whether to add the extracted HWPSS statistics to `aman` as new axes. Default is `True`. hwpss_stats_name : str, optional @@ -124,7 +134,7 @@ def get_hwpss(aman, signal=None, hwp_angle=None, bin_signal=True, bins=360, hwp_angle_bin_centers, bin_counts, binned_hwpss, hwpss_sigma_bin = get_binned_hwpss( aman, signal, hwp_angle=None, bins=bins, flags=flags, apodize_edges=apodize_edges, apodize_edges_samps=apodize_edges_samps, - apodize_flags=apodize_flags, apodize_flags_samps=apodize_flags_samps,) + apodize_flags=apodize_flags, apodize_flags_samps=apodize_flags_samps, apo_type=apo_type) # check bin count num_invalid_bins = np.count_nonzero(np.isnan(binned_hwpss[0][:])) @@ -196,7 +206,7 @@ def get_hwpss(aman, signal=None, hwp_angle=None, bin_signal=True, bins=360, def get_binned_hwpss(aman, signal=None, hwp_angle=None, bins=360, flags=None, apodize_edges=True, apodize_edges_samps=1600, - apodize_flags=True, apodize_flags_samps=200): + apodize_flags=True, apodize_flags_samps=200, apo_type='C1'): """ Bin time-ordered data by the HWP angle and return the binned signal and its standard deviation. @@ -222,6 +232,8 @@ def get_binned_hwpss(aman, signal=None, hwp_angle=None, If True, applies an apodization window based on the flags. Defaults to True. apodize_flags_samps : int, optional The number of samples over which to apply the flags apodization window. Defaults to 200. + apo_type: str, optional + Type of apodization, default is C1. Returns ------- @@ -236,35 +248,22 @@ def get_binned_hwpss(aman, signal=None, hwp_angle=None, if hwp_angle is None: hwp_angle = aman['hwp_angle'] + if isinstance(flags, str): + flags = aman.flags.get(flags) + + weight_for_signal = None + if apodize_flags and (flags is not None): + weight_for_signal = apodize.get_apodize_window_from_flags( + aman, flags=flags, apodize_samps=apodize_flags_samps, apo_type=apo_type) + if apodize_edges: - weight_for_signal = apodize.get_apodize_window_for_ends(aman, apodize_samps=apodize_edges_samps) - if isinstance(flags, str): - flags = aman.flags.get(flags) - if (flags is not None) and apodize_flags: - flags_mask = flags.mask() - if flags_mask.ndim == 1: - flag_is_1d = True - else: - all_columns_same = np.all(np.all(flags_mask == flags_mask[0, :], axis=0)) - if all_columns_same: - flag_is_1d = True - flags_mask = flags_mask[0] - else: - flag_is_1d = False - if flag_is_1d: - weight_for_signal = weight_for_signal * apodize.get_apodize_window_from_flags(aman, - flags=flags, - apodize_samps=apodize_flags_samps) - else: - weight_for_signal = weight_for_signal[np.newaxis, :] * apodize.get_apodize_window_from_flags(aman, - flags=flags, - apodize_samps=apodize_flags_samps) - else: - if (flags is not None) and apodize_flags: - weight_for_signal = apodize.get_apodize_window_from_flags(aman, flags=flags, apodize_samps=apodize_flags_samps) + edges_apodizer = apodize.get_apodize_window_for_ends( + aman, apodize_samps=apodize_edges_samps, apo_type=apo_type) + if weight_for_signal is None: + weight_for_signal = edges_apodizer else: - weight_for_signal = None - + weight_for_signal *= edges_apodizer + binning_dict = tod_ops.bin_signal(aman, bin_by=hwp_angle, range=[0, 2*np.pi], bins=bins, signal=signal, flags=flags, weight_for_signal=weight_for_signal) @@ -709,6 +708,7 @@ def get_tau_hwp( bpf_cfg=None, width=1000, apodize_samps=2000, + apo_type='C1', trim_samps=2000, min_fhwp=1., max_fhwp=2., @@ -796,7 +796,7 @@ def get_tau_hwp( tod_ops.detrend_tod(aman, signal_name=signal, method="median") tod_ops.apodize.apodize_cosine(aman, signal_name=signal, - apodize_samps=apodize_samps) + apodize_samps=apodize_samps, apo_type=apo_type) demod_tod(aman, signal=signal, demod_mode=demod_mode, lpf_cfg=lpf_cfg, bpf_cfg=bpf_cfg, wrap=True) diff --git a/sotodlib/preprocess/processes.py b/sotodlib/preprocess/processes.py index 649d99b0f..cd7fa8ef8 100644 --- a/sotodlib/preprocess/processes.py +++ b/sotodlib/preprocess/processes.py @@ -1194,6 +1194,7 @@ class Apodize(_Preprocess): signal_name: signal apodize_samps: 2000 flags: glitch_flags + apo_type: C1 .. autofunction:: sotodlib.tod_ops.apodize.apodize_cosine """ diff --git a/sotodlib/tod_ops/apodize.py b/sotodlib/tod_ops/apodize.py index 8ffa44a5a..37d90acfc 100644 --- a/sotodlib/tod_ops/apodize.py +++ b/sotodlib/tod_ops/apodize.py @@ -1,25 +1,30 @@ import numpy as np -def get_apodize_window_for_ends(aman, apodize_samps=1600): +def get_apodize_window_for_ends(aman, apodize_samps=1600, apo_type='C1'): """ Generate an apodization window using a cosine taper at the beginning and end. Args: aman: An axismanager apodize_samps (int): Number of samples to apply the cosine taper to at each end. + apo_type: Type of apodization, default is C1. Returns: numpy.ndarray: An array representing the apodization window. """ + if apo_type == 'C1': + cosedge = 0.5 * (np.cos(np.linspace(0, np.pi, apodize_samps)) + 1) + elif apo_type == 'old_default': + cosedge = np.cos(np.linspace(0, np.pi/2, apodize_samps)) + w = np.ones(aman.samps.count) - cosedge = np.cos(np.linspace(0, np.pi/2, apodize_samps)) w[-apodize_samps:] = cosedge w[:apodize_samps] = np.flip(cosedge) return w -def get_apodize_window_from_flags(aman, flags, apodize_samps=200): +def get_apodize_window_from_flags(aman, flags, apodize_samps=200, apo_type='C1'): """ Generate an apodization window based on flag values. Apply cosine tapering every continuous portion of data between flagged region. @@ -29,6 +34,7 @@ def get_apodize_window_from_flags(aman, flags, apodize_samps=200): flags (str or RangesMatrix or Ranges): Flags of mask in RangesMatrix/Ranges. If provided by a string, 'aman.flags[flags]' is used for the flags. apodize_samps (int): Number of samples to apply the cosine taper. + apo_type: Type of apodization, default is C1. Returns: numpy.ndarray: An array representing the apodization window. @@ -47,9 +53,13 @@ def get_apodize_window_from_flags(aman, flags, apodize_samps=200): else: flag_is_1d = False + if apo_type == 'C1': + cosedge = 0.5 * (np.cos(np.linspace(0, np.pi, apodize_samps)) + 1) + elif apo_type == 'old_default': + cosedge = np.cos(np.linspace(0, np.pi/2, apodize_samps)) + apodizer = ~flags_mask apodizer = apodizer.astype(float) - cosedge = np.cos(np.linspace(0, np.pi/2, apodize_samps)) if flag_is_1d: idxes_left = np.where(np.diff(apodizer) == -1)[0] @@ -91,7 +101,7 @@ def get_apodize_window_from_flags(aman, flags, apodize_samps=200): def apodize_cosine(aman, signal_name='signal', apodize_samps=1600, in_place=True, - apo_axis='apodized', window=None, flags=None): + apo_axis='apodized', window=None, flags=None, apo_type='C1'): """ Function to smoothly filter the timestream to 0's on the ends with a cosine function. If window is provided, multiply the window function to @@ -105,12 +115,13 @@ def apodize_cosine(aman, signal_name='signal', apodize_samps=1600, in_place=True apo_axis (str): Axis to store the apodized signal if not in place. window (numpy.ndarray): Precomputed apodization window. flags (str or RangesMatrix or Ranges): flag value to compute apodization window. + apo_type: Type of apodization, default is C1. """ if window is None: if flags is not None: - window = get_apodize_window_from_flags(aman, flags, apodize_samps) + window = get_apodize_window_from_flags(aman, flags, apodize_samps, apo_type=apo_type) else: - window = get_apodize_window_for_ends(aman, apodize_samps) + window = get_apodize_window_for_ends(aman, apodize_samps, apo_type=apo_type) if in_place: aman[signal_name] *= window diff --git a/sotodlib/tod_ops/azss.py b/sotodlib/tod_ops/azss.py index f9bbe29ae..35789ef9e 100644 --- a/sotodlib/tod_ops/azss.py +++ b/sotodlib/tod_ops/azss.py @@ -58,7 +58,7 @@ def _valid_dets_mask(azss_stats, min_valid_bins=1): def bin_by_az(aman, signal=None, az=None, azrange=None, bins=100, flags=None, apodize_edges=True, apodize_edges_samps=1600, - apodize_flags=True, apodize_flags_samps=200): + apodize_flags=True, apodize_flags_samps=200, apo_type='C1'): """ Bins a signal by azimuth angle. @@ -99,36 +99,19 @@ def bin_by_az(aman, signal=None, az=None, azrange=None, bins=100, flags=None, * 'binned_signal': binned signal * 'binned_signal_sigma': estimated sigma of binned signal """ + weight_for_signal = None + if apodize_flags and (flags is not None): + weight_for_signal = apodize.get_apodize_window_from_flags( + aman, flags=flags, apodize_samps=apodize_flags_samps, apo_type=apo_type) + if apodize_edges: - weight_for_signal = apodize.get_apodize_window_for_ends(aman, apodize_samps=apodize_edges_samps) - if isinstance(flags, str): - flags = aman.flags.get(flags) - if (flags is not None) and apodize_flags: - flags_mask = flags.mask() - # check the flags dimension - if flags_mask.ndim == 1: - flag_is_1d = True - else: - all_columns_same = np.all(np.all(flags_mask == flags_mask[0, :], axis=0)) - if all_columns_same: - flag_is_1d = True - flags_mask = flags_mask[0] - else: - flag_is_1d = False - - if flag_is_1d: - weight_for_signal = weight_for_signal * apodize.get_apodize_window_from_flags(aman, - flags=flags, - apodize_samps=apodize_flags_samps) - else: - weight_for_signal = weight_for_signal[np.newaxis, :] * apodize.get_apodize_window_from_flags(aman, - flags=flags, - apodize_samps=apodize_flags_samps) - else: - if (flags is not None) and apodize_flags: - weight_for_signal = apodize.get_apodize_window_from_flags(aman, flags=flags, apodize_samps=apodize_flags_samps) + edges_apodizer = apodize.get_apodize_window_for_ends( + aman, apodize_samps=apodize_edges_samps, apo_type=apo_type) + if weight_for_signal is None: + weight_for_signal = edges_apodizer else: - weight_for_signal = None + weight_for_signal *= edges_apodizer + binning_dict = bin_signal(aman, bin_by=az, signal=signal, range=azrange, bins=bins, flags=flags, weight_for_signal=weight_for_signal) return binning_dict @@ -280,7 +263,7 @@ def fit_azss(az, azss_stats, max_mode, modes_axis_name='azss_modes', fit_range=N def get_azss(aman, signal='signal', az=None, azrange=None, bins=100, flags=None, scan_flags=None, - apodize_edges=True, apodize_edges_samps=1600, apodize_flags=True, apodize_flags_samps=200, + apodize_edges=True, apodize_edges_samps=1600, apodize_flags=True, apodize_flags_samps=200, apo_type='C1', apply_prefilt=True, prefilt_cfg=None, prefilt_detrend='linear', method='interpolate', max_mode=None, modes_axis_name='azss_modes', subtract_in_place=False, merge_stats=True, azss_stats_name='azss_stats', @@ -322,6 +305,8 @@ def get_azss(aman, signal='signal', az=None, azrange=None, bins=100, flags=None, If True, applies an apodization window based on the flags. Defaults to True. apodize_flags_samps : int, optional The number of samples over which to apply the flags apodization window. Defaults to 200. + apo_type: str, optional + Type of apodization, default is C1. apply_prefilt : bool, optional If True, applies a pre-filter to the signal before azss extraction. Defaults to True. prefilt_cfg : dict, optional @@ -401,7 +386,8 @@ def get_azss(aman, signal='signal', az=None, azrange=None, bins=100, flags=None, # do binning binning_dict = bin_by_az(aman, signal=signal, az=az, azrange=azrange, bins=bins, flags=flags, apodize_edges=apodize_edges, apodize_edges_samps=apodize_edges_samps, - apodize_flags=apodize_flags, apodize_flags_samps=apodize_flags_samps,) + apodize_flags=apodize_flags, apodize_flags_samps=apodize_flags_samps, + apo_type=apo_type) bin_centers = binning_dict['bin_centers'] bin_counts = binning_dict['bin_counts'] binned_signal = binning_dict['binned_signal'] diff --git a/sotodlib/tod_ops/t2pleakage.py b/sotodlib/tod_ops/t2pleakage.py index fb87fb931..8d1c9f32d 100644 --- a/sotodlib/tod_ops/t2pleakage.py +++ b/sotodlib/tod_ops/t2pleakage.py @@ -2,7 +2,7 @@ """ import numpy as np from sotodlib import core -from sotodlib.tod_ops import filters, apodize +from sotodlib.tod_ops import filters from sotodlib.tod_ops.fft_ops import calc_psd, calc_wn from scipy.odr import ODR, Model, RealData from lmfit import Model as LmfitModel From b8cc5d7b030aaf5a54a0d585b4d692b9b3a71ba0 Mon Sep 17 00:00:00 2001 From: ykyohei <38639108+ykyohei@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:06:57 -0400 Subject: [PATCH 2/3] Update docstring --- sotodlib/hwp/hwp.py | 6 ++++-- sotodlib/tod_ops/apodize.py | 22 +++++++++++++++++++--- sotodlib/tod_ops/azss.py | 4 +++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/sotodlib/hwp/hwp.py b/sotodlib/hwp/hwp.py index 1ce47f556..b44c95f3f 100644 --- a/sotodlib/hwp/hwp.py +++ b/sotodlib/hwp/hwp.py @@ -61,7 +61,7 @@ def get_hwpss(aman, signal=None, hwp_angle=None, bin_signal=True, bins=360, apodize_flags_samps : int, optional The number of samples over which to apply the flags apodization window. Defaults to 200. apo_type: str, optional - Type of apodization, default is C1. + Type of apodization, default is C1. See tod_ops.apodize for all options. merge_stats : bool, optional Whether to add the extracted HWPSS statistics to `aman` as new axes. Default is `True`. hwpss_stats_name : str, optional @@ -233,7 +233,7 @@ def get_binned_hwpss(aman, signal=None, hwp_angle=None, apodize_flags_samps : int, optional The number of samples over which to apply the flags apodization window. Defaults to 200. apo_type: str, optional - Type of apodization, default is C1. + Type of apodization, default is C1. See tod_ops.apodize for all options. Returns ------- @@ -739,6 +739,8 @@ def get_tau_hwp( width of single section of TOD. apodize_samps: int optional Number of samples on tod ends to apodize. + apo_type: str, optional + Type of apodization, default is C1. See tod_ops.apodize for all options. trim_samps: int optional Number of samples on tod ends to trim. min_fhwp: float optional diff --git a/sotodlib/tod_ops/apodize.py b/sotodlib/tod_ops/apodize.py index 37d90acfc..ffb0e6833 100644 --- a/sotodlib/tod_ops/apodize.py +++ b/sotodlib/tod_ops/apodize.py @@ -8,7 +8,12 @@ def get_apodize_window_for_ends(aman, apodize_samps=1600, apo_type='C1'): Args: aman: An axismanager apodize_samps (int): Number of samples to apply the cosine taper to at each end. - apo_type: Type of apodization, default is C1. + apo_type: (str): Type of apodization window applied to the edges. Options are: + - ``'C1'``: Standard cosine (Hann) taper, i.e. a half-cosine that + goes smoothly from 1 to 0 as ``0.5 * (1 + cos(x))`` over the + apodization region. This is the default. + - ``'old_default'``: Legacy quarter-cosine taper, ``cos(x)`` over + ``[0, pi/2]``. Retained for backward compatibility. Returns: numpy.ndarray: An array representing the apodization window. @@ -34,7 +39,12 @@ def get_apodize_window_from_flags(aman, flags, apodize_samps=200, apo_type='C1') flags (str or RangesMatrix or Ranges): Flags of mask in RangesMatrix/Ranges. If provided by a string, 'aman.flags[flags]' is used for the flags. apodize_samps (int): Number of samples to apply the cosine taper. - apo_type: Type of apodization, default is C1. + apo_type: (str): Type of apodization window applied to the edges. Options are: + - ``'C1'``: Standard cosine (Hann) taper, i.e. a half-cosine that + goes smoothly from 1 to 0 as ``0.5 * (1 + cos(x))`` over the + apodization region. This is the default. + - ``'old_default'``: Legacy quarter-cosine taper, ``cos(x)`` over + ``[0, pi/2]``. Retained for backward compatibility. Returns: numpy.ndarray: An array representing the apodization window. @@ -115,7 +125,13 @@ def apodize_cosine(aman, signal_name='signal', apodize_samps=1600, in_place=True apo_axis (str): Axis to store the apodized signal if not in place. window (numpy.ndarray): Precomputed apodization window. flags (str or RangesMatrix or Ranges): flag value to compute apodization window. - apo_type: Type of apodization, default is C1. + apo_type: (str): Type of apodization window applied to the edges. Options are: + - ``'C1'``: Standard cosine (Hann) taper, i.e. a half-cosine that + goes smoothly from 1 to 0 as ``0.5 * (1 + cos(x))`` over the + apodization region. This is the default. + - ``'old_default'``: Legacy quarter-cosine taper, ``cos(x)`` over + ``[0, pi/2]``. Retained for backward compatibility. + """ if window is None: if flags is not None: diff --git a/sotodlib/tod_ops/azss.py b/sotodlib/tod_ops/azss.py index 35789ef9e..3bbd6928f 100644 --- a/sotodlib/tod_ops/azss.py +++ b/sotodlib/tod_ops/azss.py @@ -89,6 +89,8 @@ def bin_by_az(aman, signal=None, az=None, azrange=None, bins=100, flags=None, If True, applies an apodization window based on the flags. Defaults to True. apodize_flags_samps : int, optional The number of samples over which to apply the flags apodization window. Defaults to 200. + apo_type: str, optional + Type of apodization, default is C1. See tod_ops.apodize for all options. Returns ------- @@ -306,7 +308,7 @@ def get_azss(aman, signal='signal', az=None, azrange=None, bins=100, flags=None, apodize_flags_samps : int, optional The number of samples over which to apply the flags apodization window. Defaults to 200. apo_type: str, optional - Type of apodization, default is C1. + Type of apodization, default is C1. See tod_ops.apodize for all options. apply_prefilt : bool, optional If True, applies a pre-filter to the signal before azss extraction. Defaults to True. prefilt_cfg : dict, optional From fa96c0604698f88fe6f8feff706e0c6afda286a3 Mon Sep 17 00:00:00 2001 From: ykyohei <38639108+ykyohei@users.noreply.github.com> Date: Fri, 3 Jul 2026 15:19:18 -0400 Subject: [PATCH 3/3] fix typo --- sotodlib/tod_ops/apodize.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sotodlib/tod_ops/apodize.py b/sotodlib/tod_ops/apodize.py index ffb0e6833..3d7295aec 100644 --- a/sotodlib/tod_ops/apodize.py +++ b/sotodlib/tod_ops/apodize.py @@ -8,7 +8,7 @@ def get_apodize_window_for_ends(aman, apodize_samps=1600, apo_type='C1'): Args: aman: An axismanager apodize_samps (int): Number of samples to apply the cosine taper to at each end. - apo_type: (str): Type of apodization window applied to the edges. Options are: + apo_type (str): Type of apodization window applied to the edges. Options are: - ``'C1'``: Standard cosine (Hann) taper, i.e. a half-cosine that goes smoothly from 1 to 0 as ``0.5 * (1 + cos(x))`` over the apodization region. This is the default. @@ -39,7 +39,7 @@ def get_apodize_window_from_flags(aman, flags, apodize_samps=200, apo_type='C1') flags (str or RangesMatrix or Ranges): Flags of mask in RangesMatrix/Ranges. If provided by a string, 'aman.flags[flags]' is used for the flags. apodize_samps (int): Number of samples to apply the cosine taper. - apo_type: (str): Type of apodization window applied to the edges. Options are: + apo_type (str): Type of apodization window applied to the edges. Options are: - ``'C1'``: Standard cosine (Hann) taper, i.e. a half-cosine that goes smoothly from 1 to 0 as ``0.5 * (1 + cos(x))`` over the apodization region. This is the default. @@ -125,7 +125,7 @@ def apodize_cosine(aman, signal_name='signal', apodize_samps=1600, in_place=True apo_axis (str): Axis to store the apodized signal if not in place. window (numpy.ndarray): Precomputed apodization window. flags (str or RangesMatrix or Ranges): flag value to compute apodization window. - apo_type: (str): Type of apodization window applied to the edges. Options are: + apo_type (str): Type of apodization window applied to the edges. Options are: - ``'C1'``: Standard cosine (Hann) taper, i.e. a half-cosine that goes smoothly from 1 to 0 as ``0.5 * (1 + cos(x))`` over the apodization region. This is the default.