From a829445fb5a574ed421dbc99a6c6db7c1f55f8f9 Mon Sep 17 00:00:00 2001 From: Michael McCrackan Date: Tue, 17 Mar 2026 08:31:46 -0700 Subject: [PATCH 1/7] det_match updates --- sotodlib/coords/det_match.py | 56 +++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/sotodlib/coords/det_match.py b/sotodlib/coords/det_match.py index 90107e92b..a6908e48f 100644 --- a/sotodlib/coords/det_match.py +++ b/sotodlib/coords/det_match.py @@ -196,6 +196,7 @@ def apply_design_properties(smurf_res, design_res, in_place=False, apply_pointin 'mux_bondpad', 'mux_subband', 'mux_band', 'mux_channel', 'mux_layout_pos' ] + if apply_pointing: design_props += ['xi', 'eta', 'gamma'] @@ -613,10 +614,11 @@ class MatchParams: unassigned_slots: int = 1000 freq_offset_mhz: float = 0.0 freq_width: float = 2. - dist_width: float =0.01 + dist_width: float = 0.01 unmatched_good_res_pen: float = 10. good_res_qi_thresh: float = 100e3 enforce_pointing_reqs: bool = False + allow_unassigned_to_assigned: bool = True assigned_bg_unmatched_pen: float = 100000 unassigned_bg_unmatched_pen: float = 10000 @@ -706,6 +708,11 @@ def _get_biadjacency_matrix(self) -> np.ndarray: m = src_arr['is_north'][:, None] != dst_arr['is_north'][None, :] mat[m] = np.inf + src_no_match = np.isin(src_arr['det_type'], ['NC']) + dst_no_match = np.isin(dst_arr['det_type'], ['NC']) + mat[src_no_match, :] = np.inf + mat[:, dst_no_match] = np.inf + if self.match_pars.enforce_pointing_reqs: # Det types of DARK and SLOT are allowed to may or may not have # pointing data. For these types of detectors, the cost is left @@ -714,11 +721,6 @@ def _get_biadjacency_matrix(self) -> np.ndarray: src_has_pointing = np.isfinite(src_arr['xi']) & np.isfinite(src_arr['eta']) dst_has_pointing = np.isfinite(dst_arr['xi']) & np.isfinite(dst_arr['eta']) - src_no_match = np.isin(src_arr['det_type'], ['NC']) - dst_no_match = np.isin(dst_arr['det_type'], ['NC']) - mat[src_no_match, :] = np.inf - mat[:, dst_no_match] = np.inf - src_pointing_forbidden = np.isin(src_arr['det_type'], ['UNRT', 'SQID', 'BARE']) dst_pointing_forbidden = np.isin(dst_arr['det_type'], ['UNRT', 'SQID', 'BARE']) m = src_pointing_forbidden[:, None] & dst_has_pointing[None, :] @@ -733,19 +735,51 @@ def _get_biadjacency_matrix(self) -> np.ndarray: m = (~src_has_pointing[:, None]) & dst_pointing_required[None, :] mat[m] = np.inf + # These should always have BG = -1 + src_unassigned_type = np.isin(src_arr['det_type'], ['UNRT', 'SQID', 'BARE']) + dst_unassigned_type = np.isin(dst_arr['det_type'], ['UNRT', 'SQID', 'BARE']) + # pointing or tuneset have unassigned type (shouldn't ever happen) + m = src_unassigned_type[:, None] & (dst_arr['bg'][None, :] !=-1) + mat[m] = np.inf + # Design or pointing have unassigned type (should happen) + m = dst_unassigned_type[None, :] & (src_arr['bg'][:, None] !=-1) + mat[m] = np.inf + # Frequency offset df = src_arr['res_freq'][:, None] - dst_arr['res_freq'][None, :] df -= self.match_pars.freq_offset_mhz mat += np.exp((np.abs(df / self.match_pars.freq_width)) ** 2) # BG mismatch - bgs_mismatch = src_arr['bg'][:, None] != dst_arr['bg'][None, :] - bgs_unassigned = (src_arr['bg'][:, None] == -1) | (dst_arr['bg'][None, :] == -1) + # bgs_mismatch = src_arr['bg'][:, None] != dst_arr['bg'][None, :] + # bgs_unassigned = (src_arr['bg'][:, None] == -1) | (dst_arr['bg'][None, :] == -1) + + # m = bgs_mismatch & bgs_unassigned + # mat[m] += self.match_pars.unassigned_bg_mismatch_pen + # m = bgs_mismatch & (~bgs_unassigned) + # mat[m] += self.match_pars.assigned_bg_mismatch_pen + + # design or pointing unassigned + dst_unassigned = (dst_arr['bg'][None, :] == -1) + # pointing or tune unassigned + src_unassigned = (src_arr['bg'][:, None] == -1) + + # whether or not to match unassigned bg to assigned bgs + # don't want matches when matching from design to pointing + m = dst_unassigned & (~src_unassigned) + if not self.match_pars.allow_unassigned_to_assigned: + mat[m] = np.inf + else: + mat[m] += self.match_pars.unassigned_bg_mismatch_pen - m = bgs_mismatch & bgs_unassigned + # match assigned bg to unassigned bg + m = (~dst_unassigned) & src_unassigned mat[m] += self.match_pars.unassigned_bg_mismatch_pen - m = bgs_mismatch & (~bgs_unassigned) - mat[m] += self.match_pars.assigned_bg_mismatch_pen + + # assigned bgs must not be mismatched + bgs_mismatch = src_arr['bg'][:, None] != dst_arr['bg'][None, :] + m = bgs_mismatch & (~dst_unassigned) & (~src_unassigned) + mat[m] = np.inf # If pointing, add cost if assigned too far dd = np.sqrt( From 5bb46011bcd9556fba23390c7d59d2763f06598b Mon Sep 17 00:00:00 2001 From: Michael McCrackan Date: Fri, 17 Apr 2026 12:56:57 -0700 Subject: [PATCH 2/7] more updates --- sotodlib/coords/det_match.py | 108 ++++++++++++++------- sotodlib/site_pipeline/update_det_match.py | 36 +++++-- 2 files changed, 100 insertions(+), 44 deletions(-) diff --git a/sotodlib/coords/det_match.py b/sotodlib/coords/det_match.py index a6908e48f..0a3107e07 100644 --- a/sotodlib/coords/det_match.py +++ b/sotodlib/coords/det_match.py @@ -1,6 +1,6 @@ import warnings from dataclasses import dataclass, fields, asdict, field -from typing import List, Optional, Tuple, Iterator, Union +from typing import List, Optional, Tuple, Iterator, Union, get_origin, get_args from copy import deepcopy import h5py @@ -147,10 +147,10 @@ class Resonator: bg: int = -1 det_x: float = np.nan det_y: float = np.nan - det_row: int = 0 - det_col: int = 0 + det_row: Optional[int] = None + det_col: Optional[int] = None pixel_num: int = 0 - det_rhomb: str = '' + det_rhomb: Optional[str] = None det_pol: str = '' det_freq: int = 0 det_bandpass: str = '' @@ -190,13 +190,18 @@ def apply_design_properties(smurf_res, design_res, in_place=False, apply_pointin r = deepcopy(smurf_res) design_props = [ - 'bg', 'det_x', 'det_y', 'det_row', 'det_col', 'pixel_num', 'det_rhomb', + 'bg', 'det_x', 'det_y', 'pixel_num', 'det_pol', 'det_freq', 'det_bandpass', 'det_angle_raw_deg', 'det_angle_actual_deg', 'det_type', 'det_id', 'is_optical', 'mux_bondpad', 'mux_subband', 'mux_band', 'mux_channel', 'mux_layout_pos' ] + # for LF + for attr in ('det_row', 'det_col', 'det_rhomb'): + if getattr(design_res, attr, None) is not None: + design_props.append(attr) + if apply_pointing: design_props += ['xi', 'eta', 'gamma'] @@ -268,6 +273,8 @@ def as_array(self): dtype = [] data = [] for field in fields(Resonator): + if get_origin(field.type) is Union and type(None) in get_args(field.type): + continue if field.type == str: typ = ' np.ndarray: - src_arr = self.src.as_array() - dst_arr = self.dst.as_array() + src_arr = self.src.as_array() # pointing or tuneset + dst_arr = self.dst.as_array() # design or pointing mat = np.zeros((len(self.src), len(self.dst)), dtype=float) @@ -721,6 +760,7 @@ def _get_biadjacency_matrix(self) -> np.ndarray: src_has_pointing = np.isfinite(src_arr['xi']) & np.isfinite(src_arr['eta']) dst_has_pointing = np.isfinite(dst_arr['xi']) & np.isfinite(dst_arr['eta']) + # UNRT, SQID, and BARE must not have pointing src_pointing_forbidden = np.isin(src_arr['det_type'], ['UNRT', 'SQID', 'BARE']) dst_pointing_forbidden = np.isin(dst_arr['det_type'], ['UNRT', 'SQID', 'BARE']) m = src_pointing_forbidden[:, None] & dst_has_pointing[None, :] @@ -728,6 +768,7 @@ def _get_biadjacency_matrix(self) -> np.ndarray: m = src_has_pointing[:, None] & dst_pointing_forbidden[None, :] mat[m] = np.inf + # OPTC must have pointing src_pointing_required = np.isin(src_arr['det_type'], ['OPTC']) dst_pointing_required = np.isin(dst_arr['det_type'], ['OPTC']) m = src_pointing_required[:, None] & (~dst_has_pointing[None, :]) @@ -738,10 +779,12 @@ def _get_biadjacency_matrix(self) -> np.ndarray: # These should always have BG = -1 src_unassigned_type = np.isin(src_arr['det_type'], ['UNRT', 'SQID', 'BARE']) dst_unassigned_type = np.isin(dst_arr['det_type'], ['UNRT', 'SQID', 'BARE']) - # pointing or tuneset have unassigned type (shouldn't ever happen) + # pointing or tuneset have unassigned type (shouldn't ever happen since + # they won't have det_types yet) m = src_unassigned_type[:, None] & (dst_arr['bg'][None, :] !=-1) mat[m] = np.inf - # Design or pointing have unassigned type (should happen) + # Design or pointing have unassigned type (should happen since dst + # will have det_types) m = dst_unassigned_type[None, :] & (src_arr['bg'][:, None] !=-1) mat[m] = np.inf @@ -750,21 +793,12 @@ def _get_biadjacency_matrix(self) -> np.ndarray: df -= self.match_pars.freq_offset_mhz mat += np.exp((np.abs(df / self.match_pars.freq_width)) ** 2) - # BG mismatch - # bgs_mismatch = src_arr['bg'][:, None] != dst_arr['bg'][None, :] - # bgs_unassigned = (src_arr['bg'][:, None] == -1) | (dst_arr['bg'][None, :] == -1) - - # m = bgs_mismatch & bgs_unassigned - # mat[m] += self.match_pars.unassigned_bg_mismatch_pen - # m = bgs_mismatch & (~bgs_unassigned) - # mat[m] += self.match_pars.assigned_bg_mismatch_pen - - # design or pointing unassigned + # Design or pointing unassigned dst_unassigned = (dst_arr['bg'][None, :] == -1) # pointing or tune unassigned src_unassigned = (src_arr['bg'][:, None] == -1) - # whether or not to match unassigned bg to assigned bgs + # Whether or not to match unassigned bg to assigned bgs # don't want matches when matching from design to pointing m = dst_unassigned & (~src_unassigned) if not self.match_pars.allow_unassigned_to_assigned: @@ -772,11 +806,11 @@ def _get_biadjacency_matrix(self) -> np.ndarray: else: mat[m] += self.match_pars.unassigned_bg_mismatch_pen - # match assigned bg to unassigned bg + # Match assigned bg to unassigned bg m = (~dst_unassigned) & src_unassigned mat[m] += self.match_pars.unassigned_bg_mismatch_pen - # assigned bgs must not be mismatched + # Assigned bgs must not be mismatched bgs_mismatch = src_arr['bg'][:, None] != dst_arr['bg'][None, :] m = bgs_mismatch & (~dst_unassigned) & (~src_unassigned) mat[m] = np.inf @@ -835,9 +869,11 @@ def _match(self): for r1, r2 in self.get_match_iter(include_unmatched=True): if r1 is None: r2.matched = 0 + r2.match_idx = -1 continue if r2 is None: r1.matched = 0 + r1.match_idx = -1 continue r1.matched = 1 diff --git a/sotodlib/site_pipeline/update_det_match.py b/sotodlib/site_pipeline/update_det_match.py index 08fca4ef9..d7af70733 100644 --- a/sotodlib/site_pipeline/update_det_match.py +++ b/sotodlib/site_pipeline/update_det_match.py @@ -25,6 +25,14 @@ logger.setLevel(logging.INFO) +def get_detset_time(detset: str) -> float: + """ + Gets timestamp associated with a detset. Will parse this from the detset + name, assuming it is of the form _