From c7debbf4974bf7cb3ebdd8d580f94abeb492c0e0 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Tue, 16 Sep 2025 15:04:27 -0400 Subject: [PATCH 01/63] making monopod version of millipede_wilks --- skymap_scanner/recos/monopod_wilks.py | 411 ++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100644 skymap_scanner/recos/monopod_wilks.py diff --git a/skymap_scanner/recos/monopod_wilks.py b/skymap_scanner/recos/monopod_wilks.py new file mode 100644 index 000000000..c8ddc82aa --- /dev/null +++ b/skymap_scanner/recos/monopod_wilks.py @@ -0,0 +1,411 @@ +"""IceTray segment for a millipede reco.""" + +# fmt: off +# pylint: skip-file +# mypy: ignore-errors + +import datetime +import os +from typing import Final, List, Tuple + +import numpy +from icecube.icetray import I3Units +from icecube import ( # noqa: F401 + VHESelfVeto, + dataclasses, + frame_object_diff, + gulliver, + gulliver_modules, + icetray, + lilliput, + millipede, + photonics_service, + recclasses, + simclasses +) +from icecube.icetray import I3Frame + +from .. import config as cfg +from ..utils.pixel_classes import RecoPixelVariation +from . import RecoInterface, VertexGenerator +from .common.pulse_proc import mask_deepcore, pulse_cleaning + + +class MillipedeWilks(RecoInterface): + """Reco logic for millipede.""" + + # Spline requirements ############################################## + FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" + FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" + FTP_EFFD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.eff.fits" + FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" + FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" + + SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, + FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] + + def __init__(self, realtime_format_version: str): + super().__init__(realtime_format_version) + self.rotate_vertex = True + self.refine_time = True + self.add_fallback_position = True + + self.pulsesName_input = self.get_input_pulses(realtime_format_version) + self.pulsesName = self.pulsesName_input + "IC" + self.pulsesName_cleaned = self.pulsesName+'LatePulseCleaned' + + + + @staticmethod + def get_vertex_variations() -> List[dataclasses.I3Position]: + """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variations. + """ + return VertexGenerator.point() + + def setup_reco(self): + datastager = self.get_datastager() + + datastager.stage_files(self.SPLINE_REQUIREMENTS) + + abs_spline: str = datastager.get_filepath(self.FTP_ABS_SPLINE) + prob_spline: str = datastager.get_filepath(self.FTP_PROB_SPLINE) + effd_spline: str = datastager.get_filepath(self.FTP_EFFD_SPLINE) + effp_spline: str = datastager.get_filepath(self.FTP_EFFP_SPLINE) + tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) + + self.cascade_service = photonics_service.I3PhotoSplineService( + abs_spline, prob_spline, timingSigma=0.0, + effectivedistancetable = effd_spline, + tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), + quantileEpsilon=1, + effectivedistancetableprob = effp_spline, + effectivedistancetabletmod = tmod_spline + ) + + self.muon_service = None + + @icetray.traysegment + def prepare_frames(self, tray, name, logger): + # Generates the vertex seed for the initial scan. + # Only run if HESE_VHESelfVeto is not present in the frame. + # VertexThreshold is 250 in the original HESE analysis (Tianlu) + # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. + def extract_seed(frame): + seed_prefix = "HESE_VHESelfVeto" + frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] + frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] + + tray.Add(extract_seed, "ExtractSeed", + If = lambda frame: frame.Has("HESE_VHESelfVeto")) + + tray.AddModule('VHESelfVeto', 'selfveto', + VertexThreshold=250, + Pulses=self.pulsesName_input+'HLC', + OutputBool='HESE_VHESelfVeto', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: "HESE_VHESelfVeto" not in frame) + + # this only runs if the previous module did not return anything + tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', + VertexThreshold=5, + Pulses=self.pulsesName_input+'HLC', + OutputBool='VHESelfVeto_meaningless_lowen', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: not frame.Has("HESE_VHESelfVeto")) + + tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) + + @staticmethod + def makeSurePulsesExist(frame, pulsesName) -> None: + if pulsesName not in frame: + raise RuntimeError(f"{pulsesName} not in frame") + if pulsesName + "TimeWindows" not in frame: + raise RuntimeError(f"{pulsesName + 'TimeWindows'} not in frame") + if pulsesName + "TimeRange" not in frame: + raise RuntimeError(f"{pulsesName + 'TimeRange'} not in frame") + + @icetray.traysegment + def exclusions(self, tray, name): + tray.Add('Delete', keys=['BrightDOMs', + 'SaturatedDOMs', + 'DeepCoreDOMs', + self.pulsesName_cleaned, + self.pulsesName_cleaned+'TimeWindows', + self.pulsesName_cleaned+'TimeRange']) + + exclusionList = \ + tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', + Pulses = self.pulsesName, + ExcludeDeepCore='DeepCoreDOMs', + ExcludeSaturatedDOMs='SaturatedDOMs', + ExcludeBrightDOMs='BrightDOMs', + BrightDOMThreshold=2, + BadDomsList='BadDomsList', + CalibrationErrata='CalibrationErrata', + SaturationWindows='SaturationWindows' + ) + + + # I like having frame objects in there even if they are empty for some frames + def createEmptyDOMLists(frame, ListNames=[]): + for name in ListNames: + if name in frame: + continue + frame[name] = dataclasses.I3VectorOMKey() + tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', + ListNames = ["BrightDOMs"]) + # exclude bright DOMs + ExcludedDOMs = exclusionList + + def skipunhits(frame, output, pulses): + keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] + keepoms = list(range(1,60,5)) + all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame( + frame, pulses) + omgeo = frame['I3Geometry'] + geo = omgeo.omgeo + unhits = dataclasses.I3VectorOMKey() + for k, v in geo.items(): + if v.omtype != dataclasses.I3OMGeo.OMType.IceCube: + continue + if k.string not in keepstrings: + if k not in all_pulses.keys(): + unhits.append(k) + else: + if k not in all_pulses.keys() and k.om not in keepoms: + unhits.append(k) + + frame[output] = unhits + + ################## + tray.AddModule(pulse_cleaning, "LatePulseCleaning", + input_pulses_name=self.pulsesName, + output_pulses_name=self.pulsesName_cleaned, + residual=1.5e3*I3Units.ns) + ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') + + tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) + ExcludedDOMs.append('OtherUnhits') + return ExcludedDOMs + + + @icetray.traysegment + def traysegment(self, tray, name, logger, seed=None): + """Perform MillipedeWilks reco.""" + ExcludedDOMs = tray.Add(self.exclusions) + + tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) + + def check_cal(frame): + cal = frame['I3Calibration'] + logger.debug('Mean SPEs') + for omkey in list(cal.dom_cal.keys())[::100]: + x = cal.dom_cal[omkey] + mean_spe = dataclasses.mean_spe_charge(x) + logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') + logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') + tray.Add(check_cal) + + def notify0(frame): + logger.debug(f"starting a new fit ({name})! {datetime.datetime.now()}") + + tray.AddModule(notify0, "notify0") + + tray.AddService('MillipedeLikelihoodFactory', 'millipedellh', + MuonPhotonicsService=self.muon_service, + CascadePhotonicsService=self.cascade_service, + ShowerRegularization=1e-14, + ExcludedDOMs=ExcludedDOMs, + PartialExclusion=True, + ReadoutWindow=self.pulsesName_cleaned + 'TimeRange', + Pulses=self.pulsesName_cleaned, + BinSigma=2, + MinTimeWidth=25, + RelUncertainty=1) + + tray.AddService('I3GSLRandomServiceFactory', 'I3RandomService') + + tray.context['isimplex'] = lilliput.IMinuitMinimizer( + MaxIterations=2000, + Tolerance=0.01, + Algorithm="SIMPLEX", + MinuitPrintLevel=2, + MinuitPrecision=numpy.finfo('float32').eps + ) + tray.context['imigrad'] = lilliput.IMinuitMinimizer( + MaxIterations=1000, + Tolerance=0.01, + Algorithm="MIGRAD", + WithGradients=True, + FlatnessCheck=False, + IgnoreEDM=True, # Don't report convergence failures + CheckGradient=False, # Don't die on gradient errors + MinuitStrategy=0, # Don't try to check local curvature + MinuitPrintLevel=2 + ) + + coars_steps = dict(StepX=100.*I3Units.m, + StepY=100.*I3Units.m, + StepZ=100.*I3Units.m, + StepZenith=0., + StepAzimuth=0., + StepT=250.*I3Units.ns, + ShowerSpacing=5.*I3Units.m, + MuonSpacing=0, + Boundary=650*I3Units.m) + finer_steps = dict(StepX=2.*I3Units.m, + StepY=2.*I3Units.m, + StepZ=2.*I3Units.m, + StepZenith=0., + StepAzimuth=0., + StepT=5.*I3Units.ns, + ShowerSpacing=2.5*I3Units.m, + MuonSpacing=0, + Boundary=650*I3Units.m) + if seed is not None: + logger.debug('Updating StepXYZ') + self.UpdateStepXYZ(coars_steps, seed.dir, 150*I3Units.m) + self.UpdateStepXYZ(finer_steps, seed.dir, 3*I3Units.m) + tray.AddService('MuMillipedeParametrizationFactory', 'coarseSteps', **coars_steps) + + tray.AddService('I3BasicSeedServiceFactory', 'vetoseed', + FirstGuesses=[f'{cfg.OUTPUT_PARTICLE_NAME}', f'{cfg.OUTPUT_PARTICLE_NAME}_fallback'], + TimeShiftType='TNone', + PositionShiftType='None') + + tray.Add('I3SimpleFitter', + OutputName='MillipedeStarting1stPass', + SeedService='vetoseed', + Parametrization='coarseSteps', + LogLikelihood='millipedellh', + Minimizer='isimplex') + + def notify1(frame): + logger.debug(f"1st pass done! {datetime.datetime.now()}") + logger.debug(f"Seeded with: {frame[f'{cfg.OUTPUT_PARTICLE_NAME}']}") + logger.debug(f"MillipedeStarting1stPass: {frame['MillipedeStarting1stPass']}") + + tray.AddModule(notify1, "notify1") + + tray.AddService('MuMillipedeParametrizationFactory', 'fineSteps', **finer_steps) + + tray.AddService('I3BasicSeedServiceFactory', 'firstFitSeed', + FirstGuesses=['MillipedeStarting1stPass'], + TimeShiftType='TNone', + PositionShiftType='None') + + tray.Add('I3SimpleFitter', + SeedService='firstFitSeed', + OutputName='MillipedeStarting2ndPass_simplex', + Parametrization='fineSteps', + LogLikelihood='millipedellh', + Minimizer='isimplex') + + tray.AddService('I3BasicSeedServiceFactory', 'secondsimplexseed', + FirstGuesses=['MillipedeStarting2ndPass_simplex'], + TimeShiftType='TNone', + PositionShiftType='None') + + tray.Add('I3SimpleFitter', + SeedService='secondsimplexseed', + OutputName='MillipedeStarting2ndPass', + Parametrization='fineSteps', + LogLikelihood='millipedellh', + Minimizer='imigrad') + + def notify2(frame): + logger.debug(f"2nd pass done! {datetime.datetime.now()}") + logger.debug(f"MillipedeStarting2ndPass: {frame['MillipedeStarting2ndPass']}") + + tray.AddModule(notify2, "notify2") + + @staticmethod + def UpdateStepXYZ(the_steps, direction, uniform_step=15*I3Units.m): + the_steps['StepX'] = numpy.sqrt(1-direction.x**2)*uniform_step + the_steps['StepY'] = numpy.sqrt(1-direction.y**2)*uniform_step + the_steps['StepZ'] = numpy.sqrt(1-direction.z**2)*uniform_step + + @classmethod + def to_recopixelvariation(cls, frame: I3Frame, geometry: I3Frame) -> RecoPixelVariation: + # Calculate reco losses, based on load_scan_state() + reco_losses_inside, reco_losses_total = cls.get_reco_losses_inside( + p_frame=frame, g_frame=geometry, + ) + + if "MillipedeStarting2ndPass_millipedellh" not in frame: + llh = float("nan") + else: + llh = frame["MillipedeStarting2ndPass_millipedellh"].logl + return RecoPixelVariation( + nside=frame[cfg.I3FRAME_NSIDE].value, + pixel_id=frame[cfg.I3FRAME_PIXEL].value, + llh=llh, + reco_losses_inside=reco_losses_inside, + reco_losses_total=reco_losses_total, + posvar_id=frame[cfg.I3FRAME_POSVAR].value, + position=frame["MillipedeStarting2ndPass"].pos, + time=frame["MillipedeStarting2ndPass"].time, + energy=frame["MillipedeStarting2ndPass"].energy, + ) + + @staticmethod + def get_reco_losses_inside(p_frame: I3Frame, g_frame: I3Frame) -> Tuple[float, float]: + + if "MillipedeStarting2ndPass" not in p_frame: + return numpy.nan, numpy.nan + recoParticle = p_frame["MillipedeStarting2ndPass"] + + if "MillipedeStarting2ndPassParams" not in p_frame: + return numpy.nan, numpy.nan + + def getRecoLosses(vecParticles): + losses = [] + for p in vecParticles: + if not p.is_cascade: + continue + if p.energy == 0.: + continue + losses.append([p.time, p.energy]) + return losses + recoLosses = getRecoLosses(p_frame["MillipedeStarting2ndPassParams"]) + + + intersectionPoints = VHESelfVeto.IntersectionsWithInstrumentedVolume(g_frame["I3Geometry"], recoParticle) + intersectionTimes = [] + for intersectionPoint in intersectionPoints: + vecX = intersectionPoint.x - recoParticle.pos.x + vecY = intersectionPoint.y - recoParticle.pos.y + vecZ = intersectionPoint.z - recoParticle.pos.z + + prod = vecX*recoParticle.dir.x + vecY*recoParticle.dir.y + vecZ*recoParticle.dir.z + dist = numpy.sqrt(vecX**2 + vecY**2 + vecZ**2) + if prod < 0.: + dist *= -1. + intersectionTimes.append(dist/dataclasses.I3Constants.c + recoParticle.time) + + entryTime = None + exitTime = None + intersectionTimes = sorted(intersectionTimes) + if len(intersectionTimes) == 0: + return 0., 0. + + entryTime = intersectionTimes[0]-60.*I3Units.m/dataclasses.I3Constants.c + intersectionTimes = intersectionTimes[1:] + exitTime = intersectionTimes[-1]+60.*I3Units.m/dataclasses.I3Constants.c + intersectionTimes = intersectionTimes[:-1] + + totalRecoLosses = 0. + totalRecoLossesInside = 0. + for entry in recoLosses: + totalRecoLosses += entry[1] + if entryTime is not None and entry[0] < entryTime: + continue + if exitTime is not None and entry[0] > exitTime: + continue + totalRecoLossesInside += entry[1] + + return totalRecoLossesInside, totalRecoLosses + +RECO_CLASS: Final[type[RecoInterface]] = MillipedeWilks From 2ba2f2d769962238e7e6d48c4dfef12427397d07 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Tue, 16 Sep 2025 15:16:14 -0400 Subject: [PATCH 02/63] set shower spacing to 0 --- skymap_scanner/recos/monopod_wilks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skymap_scanner/recos/monopod_wilks.py b/skymap_scanner/recos/monopod_wilks.py index c8ddc82aa..60642482a 100644 --- a/skymap_scanner/recos/monopod_wilks.py +++ b/skymap_scanner/recos/monopod_wilks.py @@ -252,7 +252,7 @@ def notify0(frame): StepZenith=0., StepAzimuth=0., StepT=250.*I3Units.ns, - ShowerSpacing=5.*I3Units.m, + ShowerSpacing=0.*I3Units.m, MuonSpacing=0, Boundary=650*I3Units.m) finer_steps = dict(StepX=2.*I3Units.m, @@ -261,7 +261,7 @@ def notify0(frame): StepZenith=0., StepAzimuth=0., StepT=5.*I3Units.ns, - ShowerSpacing=2.5*I3Units.m, + ShowerSpacing=0*I3Units.m, MuonSpacing=0, Boundary=650*I3Units.m) if seed is not None: From 2ff385991aa8572a49a2cbb7da72f38e6d178b58 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 11 Mar 2026 16:23:28 -0400 Subject: [PATCH 03/63] Add test version of reco class for monopod+taupede --- skymap_scanner/recos/monopod_taupede.py | 470 ++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 skymap_scanner/recos/monopod_taupede.py diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py new file mode 100644 index 000000000..61b9af8a9 --- /dev/null +++ b/skymap_scanner/recos/monopod_taupede.py @@ -0,0 +1,470 @@ + + +import datetime +import random +import time +from typing import List, Final + +from icecube.icetray import I3Units # type: ignore[import] +from icecube import ( # type: ignore[import] # noqa: F401 + dataclasses, + frame_object_diff, + gulliver, + gulliver_modules, + icetray, + millipede, + photonics_service, + recclasses, + simclasses, +) + +from icecube.icetray import I3Frame # type: ignore[import] + +from .. import config as cfg +from ..utils.pixel_classes import RecoPixelVariation +from . import RecoInterface, VertexGenerator + +#IMPORTS FROM REC_TAU +import os +import argparse +from importlib.metadata import version + +from pprint import pformat +import numpy as np + +from icecube import dataio +from icecube import (icetray, + dataclasses, + photonics_service, + mue) # noqa: F401 +from icecube.icetray import I3Tray, I3Units +from icecube.phys_services.which_split import which_split +from icecube.millipede import HighEnergyExclusions +from icecube.spline_reco import SplineMPE +from icecube.level3_filter_cascade.level3_Recos import SPEFit + +# for level 3 muon (pulse cleaning needed for splinempe) +from icecube import level3_filter_muon # noqa: F401 + +# for srt cleaning +from icecube.STTools.seededRT.configuration_services import I3DOMLinkSeededRTConfigurationService + +# for gulliver +from icecube import lilliput +from icecube.gulliver_modules import gulliview + +from snowflake import library, unfold +from reco import skymap, dom +from reco.masks import (earlypulses, + maskdc, + maskunhits, + maskstrings, + maskdust, + pulse_cleaning) +from reco.truth import truth, druth +from reco.mlpd import (MonopodWrapper, + TaupedeWrapper, + MillipedeWrapper, + preferred, + define_splines) +from reco.seed import default_seeds + +class MonoTau(RecoInterface): + + #SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: + + FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" + FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" + FTP_EFFD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.eff.fits" + FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" + FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" + + SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, + FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] + + """Logic for a dummy reco.""" + + def __init__(self, realtime_format_version: str): + super().__init__(realtime_format_version) + self.rotate_vertex = True + self.refine_time = True + #VALUE TAKEN FROM MILLIPEDE_WILKS: + self.add_fallback_position = True + + + def get_vertex_variations() -> List[dataclasses.I3Position]: + """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variation.""" + return VertexGenerator.point() + + + + def setup_reco(self): + #SECTION TAKEN FROM MILLIPEDE_WILKS: + datastager = self.get_datastager() + + datastager.stage_files(self.SPLINE_REQUIREMENTS) + + abs_spline: str = datastager.get_filepath(self.FTP_ABS_SPLINE) + prob_spline: str = datastager.get_filepath(self.FTP_PROB_SPLINE) + effd_spline: str = datastager.get_filepath(self.FTP_EFFD_SPLINE) + effp_spline: str = datastager.get_filepath(self.FTP_EFFP_SPLINE) + tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) + + self.cascade_service = photonics_service.I3PhotoSplineService( + abs_spline, prob_spline, timingSigma=0.0, + effectivedistancetable = effd_spline, + tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), + quantileEpsilon=1, + effectivedistancetableprob = effp_spline, + effectivedistancetabletmod = tmod_spline + ) + + self.muon_service = None + + + + + + + @staticmethod + @icetray.traysegment + def prepare_frames(tray, name, logger, **kwargs) -> None: + #CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED + # Generates the vertex seed for the initial scan. + # Only run if HESE_VHESelfVeto is not present in the frame. + # VertexThreshold is 250 in the original HESE analysis (Tianlu) + # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. + def extract_seed(frame): + seed_prefix = "HESE_VHESelfVeto" + frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] + frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] + + tray.Add(extract_seed, "ExtractSeed", + If = lambda frame: frame.Has("HESE_VHESelfVeto")) + + tray.AddModule('VHESelfVeto', 'selfveto', + VertexThreshold=250, + Pulses=self.pulsesName_input+'HLC', + OutputBool='HESE_VHESelfVeto', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: "HESE_VHESelfVeto" not in frame) + + # this only runs if the previous module did not return anything + tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', + VertexThreshold=5, + Pulses=self.pulsesName_input+'HLC', + OutputBool='VHESelfVeto_meaningless_lowen', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: not frame.Has("HESE_VHESelfVeto")) + + tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) + + #OTHER METHODS FROM MILLIPEDE_WILKS: + def makeSurePulsesExist(frame, pulsesName) -> None: + if pulsesName not in frame: + raise RuntimeError(f"{pulsesName} not in frame") + if pulsesName + "TimeWindows" not in frame: + raise RuntimeError(f"{pulsesName + 'TimeWindows'} not in frame") + if pulsesName + "TimeRange" not in frame: + raise RuntimeError(f"{pulsesName + 'TimeRange'} not in frame") + + @icetray.traysegment + def exclusions(self, tray, name): + tray.Add('Delete', keys=['BrightDOMs', + 'SaturatedDOMs', + 'DeepCoreDOMs', + self.pulsesName_cleaned, + self.pulsesName_cleaned+'TimeWindows', + self.pulsesName_cleaned+'TimeRange']) + + exclusionList = \ + tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', + Pulses = self.pulsesName, + ExcludeDeepCore='DeepCoreDOMs', + ExcludeSaturatedDOMs='SaturatedDOMs', + ExcludeBrightDOMs='BrightDOMs', + BrightDOMThreshold=2, + BadDomsList='BadDomsList', + CalibrationErrata='CalibrationErrata', + SaturationWindows='SaturationWindows' + ) + + + + # I like having frame objects in there even if they are empty for some frames + def createEmptyDOMLists(frame, ListNames=[]): + for name in ListNames: + if name in frame: + continue + frame[name] = dataclasses.I3VectorOMKey() + tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', + ListNames = ["BrightDOMs"]) + # exclude bright DOMs + ExcludedDOMs = exclusionList + + def skipunhits(frame, output, pulses): + keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] + keepoms = list(range(1,60,5)) + all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame( + frame, pulses) + omgeo = frame['I3Geometry'] + geo = omgeo.omgeo + unhits = dataclasses.I3VectorOMKey() + for k, v in geo.items(): + if v.omtype != dataclasses.I3OMGeo.OMType.IceCube: + continue + if k.string not in keepstrings: + if k not in all_pulses.keys(): + unhits.append(k) + else: + if k not in all_pulses.keys() and k.om not in keepoms: + unhits.append(k) + + frame[output] = unhits + + ################## + tray.AddModule(pulse_cleaning, "LatePulseCleaning", + input_pulses_name=self.pulsesName, + output_pulses_name=self.pulsesName_cleaned, + residual=1.5e3*I3Units.ns) + ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') + + tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) + ExcludedDOMs.append('OtherUnhits') + return ExcludedDOMs + + + + + #SEPARATE METHODS FROM REC_TAU.PY: + def sane(frame, split_names): + for split_name in split_names: + if which_split(split_name=split_name)(frame): + return True + return False + + + ''' def print_frameid(frame): + eventid = frame['I3EventHeader'].event_id + print("*******Currently processing frame %s*******" %eventid)''' + + + def fixed_dir(filelist, isdata, hypo, split_names, nframes=None): + truths = [] + + def extract(frame): + truths.append(frame['cc'].dir) + tray = I3Tray() + tray.Add('I3Reader', Filenamelist=filelist) + tray.Add(sane, split_names=split_names) + if isdata: + tray.Add(druth, hypo=hypo) + else: + tray.Add(truth, hypo=hypo) + tray.Add(extract) + if nframes is None: + tray.Execute() + else: + tray.Execute(nframes) + if len(set([(_.zenith, _.azimuth) for _ in truths])) != 1: + icetray.logging.log_warn( + 'The number of extracted, unique true dirs is not 1, not updating stepXYZ') + return None + return truths[0] + + + ) + + + @staticmethod + #TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY + @icetray.traysegment + #USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION + def traysegment(self,tray, name, logger, seed): + + #TAKEN FROM MILLIPEDE_WILKS: + ExcludedDOMs = tray.Add(self.exclusions) + + tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) + + def check_cal(frame): + cal = frame['I3Calibration'] + logger.debug('Mean SPEs') + for omkey in list(cal.dom_cal.keys())[::100]: + x = cal.dom_cal[omkey] + mean_spe = dataclasses.mean_spe_charge(x) + logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') + logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') + tray.Add(check_cal) + + def notify0(frame): + logger.debug(f"starting a new fit ({name})! {datetime.datetime.now()}") + + tray.AddModule(notify0, "notify0") + + + + + + + + #BEGIN REC_TAU + wrapperfn = TaupedeWrapper + specifier = 'TaupedeFit' + loss_vector_suffix = 'Particles' + #STARTING WITH THE DEFAULT ITERATIONS NUMBER + iterations = 2 + #TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY + #USED DEFAULT FOR SPLIT NAMES + tray.Add(sane, split_names=['InIceSplit',]) + #tray.Add(print_frameid) + + #CODE TO RUN WHEN ISDATA=NONE + tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has('cc')) + + #LEAVING OUT SEED CHAIN FOR NOW + + + + #LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS + #SETTING PULSES FOR RECO TO DEFAULT + pulses_for_reco='SplitInIcePulses' + millipede_params = {'Pulses': f'{pulses_for_reco}PulseCleaned', + 'CascadePhotonicsService': self.cascade_service, + 'MuonPhotonicsService': None, + 'ExcludedDOMs': self.excludedDOMs, + 'ReadoutWindow': f'{pulses_for_reco}PulseCleanedTimeRange', + 'PartialExclusion': True, + 'PhotonsPerBin': 0, + 'UseUnhitDOMs': not False, + 'MinTimeWidth': 16, + 'BinSigma': np.nan, + 'RelUncertainty': 0.05,'StepZenith':0,'StepAzimuth':0} + icetray.logging.log_info(pformat(millipede_params), + __name__) + minis = [_ for _ in ['MIGRAD', + 'iMIGRAD', + 'SIMPLEX', + 'iSIMPLEX', + 'LBFGSB'] + sfx='PPB0' + for mini in minis: + + tray.Add(wrapperfn, + f'{mini}_{PPB0}', + Seed=Seed, + Minimizer=mini, + Unfold=False, + Chain=1, + Iterations=iterations, + **millipede_params) + seeder = lilliput.segments.add_seed_service( + tray, + millipede_params['Pulses'], + [f'{specifier}_{mini}_{PPB0}']) + minispec = mini.lower() + relerr=0.05 + minispec += f'.relerr{relerr:.2f}' + + prefs = [_ for tup in [[f'TaupedeFit_{mini}_{PPB0}', + f'MonopodFit_{mini}_{PPB0}'] + for mini in minis] + for _ in tup] + tray.Add(preferred, + i3_particles_fitparams=[(_, f'{_}FitParams') for _ in prefs], + If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs])) + + + #print( prefs ) + + #print("running HESE, with printing modules") + from segments.MillipedeWrapper import MillipedeWrapper + + # energy definition + gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" + gcdfile = dataio.I3File(gcdfilepath) + frame = gcdfile.pop_frame() + + while 'I3Geometry' not in frame: + frame = gcdfile.pop_frame() + geometry = frame['I3Geometry'].omgeo + + strings = [1, 2, 3, 4, 5, 6, 13, 21, 30, 40, 50, 59, 67, 74, 73, 72, 78, 77, 76, 75, 68, 60, 51, 41, 31, 22, 14, 7] + + outerbounds = {} + cx, cy = [], [] + for string in strings: + omkey = icetray.OMKey(string, 1) + # if geometry.has_key(omkey): + x, y = geometry[omkey].position.x, geometry[omkey].position.y + outerbounds[string] = (x, y) + cx.append(x) + cy.append(y) + cx, cy = np.asarray(cx), np.asarray(cy) + order = np.argsort(np.arctan2(cx, cy)) + outeredge_x = cx[order] + outeredge_y = cy[order] + + #print(sfx) + + +#SHOULD I TAKE THIS OUT SINCE ITS TRACK +# track reco + tray.Add('I3OMSelection', 'omselection_HESE', + InputResponse = 'SRT' + "SplitInIcePulses", + OmittedStrings = [79,80,81,82,83,84,85,86], # deepcore strings + OutputOMSelection = f'SRTSplitInIcePulses_BadOMSelectionString_{sfx}', + OutputResponse = f"SRTSplitInIcePulses_IC_Singles_{sfx}") + + tray.Add(SPEFit, f'SPEFit16_{sfx}', + Pulses = f"SRTSplitInIcePulses_IC_Singles_{sfx}", + Iterations = 16) + + del millipede_params["PhotonsPerBin"] # also input to MillipedeWrapper next, gives error if entered twice + + + # HESE millipede + tray.Add(MillipedeWrapper, f'HESEMillipedeFit_{sfx}', + seed_cascade = f'MonopodFit_iMIGRAD_{sfx}', + seed_tau = f'TaupedeFit_iMIGRAD_{sfx}', + seed_track = f'SPEFit16_{sfx}', + PhotonsPerBin = 0, + ShowerSpacing = 5, + innerboundary=550, + outerboundary=650, + outeredge_x=outeredge_x, + outeredge_y=outeredge_y, + **millipede_params) + + + # rename + tray.Add('Rename', + Keys=['SRTSplitInIcePulses_IC_Singles', f'SRTSplitInIcePulses_IC_Singles_{sfx}', + 'PreferredFit_key', f'PreferredFit_key_{sfx}', + 'PreferredFit', f"PreferredFit_{sfx}"]) + #LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW + + def notify1(frame): + logger.debug(f"reco complete! {datetime.datetime.now()}") + + tray.AddModule(notify1, "notify1") + + @staticmethod + def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariation: + return RecoPixelVariation( + nside=frame[cfg.I3FRAME_NSIDE].value, + pixel_id=frame[cfg.I3FRAME_PIXEL].value, + llh=frame["Dummy_llh"].value, + reco_losses_inside=random.random(), + reco_losses_total=random.random(), + posvar_id=frame[cfg.I3FRAME_POSVAR].value, + position=frame["Dummy_pos"], + time=frame["Dummy_time"].value, + energy=frame["Dummy_time"].value, + ) + + +# Provide a standard alias for the reconstruction class provided by this module. +RECO_CLASS: Final[type[RecoInterface]] = MonoTau From 46c9f57c79670d18d4301744e9bd3d814d72c917 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 11 Mar 2026 16:26:38 -0400 Subject: [PATCH 04/63] delete old test version of monopod reco class --- skymap_scanner/recos/monopod_wilks.py | 411 -------------------------- 1 file changed, 411 deletions(-) delete mode 100644 skymap_scanner/recos/monopod_wilks.py diff --git a/skymap_scanner/recos/monopod_wilks.py b/skymap_scanner/recos/monopod_wilks.py deleted file mode 100644 index 60642482a..000000000 --- a/skymap_scanner/recos/monopod_wilks.py +++ /dev/null @@ -1,411 +0,0 @@ -"""IceTray segment for a millipede reco.""" - -# fmt: off -# pylint: skip-file -# mypy: ignore-errors - -import datetime -import os -from typing import Final, List, Tuple - -import numpy -from icecube.icetray import I3Units -from icecube import ( # noqa: F401 - VHESelfVeto, - dataclasses, - frame_object_diff, - gulliver, - gulliver_modules, - icetray, - lilliput, - millipede, - photonics_service, - recclasses, - simclasses -) -from icecube.icetray import I3Frame - -from .. import config as cfg -from ..utils.pixel_classes import RecoPixelVariation -from . import RecoInterface, VertexGenerator -from .common.pulse_proc import mask_deepcore, pulse_cleaning - - -class MillipedeWilks(RecoInterface): - """Reco logic for millipede.""" - - # Spline requirements ############################################## - FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" - FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" - FTP_EFFD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.eff.fits" - FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" - FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" - - SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, - FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] - - def __init__(self, realtime_format_version: str): - super().__init__(realtime_format_version) - self.rotate_vertex = True - self.refine_time = True - self.add_fallback_position = True - - self.pulsesName_input = self.get_input_pulses(realtime_format_version) - self.pulsesName = self.pulsesName_input + "IC" - self.pulsesName_cleaned = self.pulsesName+'LatePulseCleaned' - - - - @staticmethod - def get_vertex_variations() -> List[dataclasses.I3Position]: - """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variations. - """ - return VertexGenerator.point() - - def setup_reco(self): - datastager = self.get_datastager() - - datastager.stage_files(self.SPLINE_REQUIREMENTS) - - abs_spline: str = datastager.get_filepath(self.FTP_ABS_SPLINE) - prob_spline: str = datastager.get_filepath(self.FTP_PROB_SPLINE) - effd_spline: str = datastager.get_filepath(self.FTP_EFFD_SPLINE) - effp_spline: str = datastager.get_filepath(self.FTP_EFFP_SPLINE) - tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) - - self.cascade_service = photonics_service.I3PhotoSplineService( - abs_spline, prob_spline, timingSigma=0.0, - effectivedistancetable = effd_spline, - tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), - quantileEpsilon=1, - effectivedistancetableprob = effp_spline, - effectivedistancetabletmod = tmod_spline - ) - - self.muon_service = None - - @icetray.traysegment - def prepare_frames(self, tray, name, logger): - # Generates the vertex seed for the initial scan. - # Only run if HESE_VHESelfVeto is not present in the frame. - # VertexThreshold is 250 in the original HESE analysis (Tianlu) - # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. - def extract_seed(frame): - seed_prefix = "HESE_VHESelfVeto" - frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] - frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] - - tray.Add(extract_seed, "ExtractSeed", - If = lambda frame: frame.Has("HESE_VHESelfVeto")) - - tray.AddModule('VHESelfVeto', 'selfveto', - VertexThreshold=250, - Pulses=self.pulsesName_input+'HLC', - OutputBool='HESE_VHESelfVeto', - OutputVertexTime=cfg.INPUT_TIME_NAME, - OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: "HESE_VHESelfVeto" not in frame) - - # this only runs if the previous module did not return anything - tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', - VertexThreshold=5, - Pulses=self.pulsesName_input+'HLC', - OutputBool='VHESelfVeto_meaningless_lowen', - OutputVertexTime=cfg.INPUT_TIME_NAME, - OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: not frame.Has("HESE_VHESelfVeto")) - - tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) - - @staticmethod - def makeSurePulsesExist(frame, pulsesName) -> None: - if pulsesName not in frame: - raise RuntimeError(f"{pulsesName} not in frame") - if pulsesName + "TimeWindows" not in frame: - raise RuntimeError(f"{pulsesName + 'TimeWindows'} not in frame") - if pulsesName + "TimeRange" not in frame: - raise RuntimeError(f"{pulsesName + 'TimeRange'} not in frame") - - @icetray.traysegment - def exclusions(self, tray, name): - tray.Add('Delete', keys=['BrightDOMs', - 'SaturatedDOMs', - 'DeepCoreDOMs', - self.pulsesName_cleaned, - self.pulsesName_cleaned+'TimeWindows', - self.pulsesName_cleaned+'TimeRange']) - - exclusionList = \ - tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', - Pulses = self.pulsesName, - ExcludeDeepCore='DeepCoreDOMs', - ExcludeSaturatedDOMs='SaturatedDOMs', - ExcludeBrightDOMs='BrightDOMs', - BrightDOMThreshold=2, - BadDomsList='BadDomsList', - CalibrationErrata='CalibrationErrata', - SaturationWindows='SaturationWindows' - ) - - - # I like having frame objects in there even if they are empty for some frames - def createEmptyDOMLists(frame, ListNames=[]): - for name in ListNames: - if name in frame: - continue - frame[name] = dataclasses.I3VectorOMKey() - tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', - ListNames = ["BrightDOMs"]) - # exclude bright DOMs - ExcludedDOMs = exclusionList - - def skipunhits(frame, output, pulses): - keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] - keepoms = list(range(1,60,5)) - all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame( - frame, pulses) - omgeo = frame['I3Geometry'] - geo = omgeo.omgeo - unhits = dataclasses.I3VectorOMKey() - for k, v in geo.items(): - if v.omtype != dataclasses.I3OMGeo.OMType.IceCube: - continue - if k.string not in keepstrings: - if k not in all_pulses.keys(): - unhits.append(k) - else: - if k not in all_pulses.keys() and k.om not in keepoms: - unhits.append(k) - - frame[output] = unhits - - ################## - tray.AddModule(pulse_cleaning, "LatePulseCleaning", - input_pulses_name=self.pulsesName, - output_pulses_name=self.pulsesName_cleaned, - residual=1.5e3*I3Units.ns) - ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') - - tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) - ExcludedDOMs.append('OtherUnhits') - return ExcludedDOMs - - - @icetray.traysegment - def traysegment(self, tray, name, logger, seed=None): - """Perform MillipedeWilks reco.""" - ExcludedDOMs = tray.Add(self.exclusions) - - tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) - - def check_cal(frame): - cal = frame['I3Calibration'] - logger.debug('Mean SPEs') - for omkey in list(cal.dom_cal.keys())[::100]: - x = cal.dom_cal[omkey] - mean_spe = dataclasses.mean_spe_charge(x) - logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') - logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') - tray.Add(check_cal) - - def notify0(frame): - logger.debug(f"starting a new fit ({name})! {datetime.datetime.now()}") - - tray.AddModule(notify0, "notify0") - - tray.AddService('MillipedeLikelihoodFactory', 'millipedellh', - MuonPhotonicsService=self.muon_service, - CascadePhotonicsService=self.cascade_service, - ShowerRegularization=1e-14, - ExcludedDOMs=ExcludedDOMs, - PartialExclusion=True, - ReadoutWindow=self.pulsesName_cleaned + 'TimeRange', - Pulses=self.pulsesName_cleaned, - BinSigma=2, - MinTimeWidth=25, - RelUncertainty=1) - - tray.AddService('I3GSLRandomServiceFactory', 'I3RandomService') - - tray.context['isimplex'] = lilliput.IMinuitMinimizer( - MaxIterations=2000, - Tolerance=0.01, - Algorithm="SIMPLEX", - MinuitPrintLevel=2, - MinuitPrecision=numpy.finfo('float32').eps - ) - tray.context['imigrad'] = lilliput.IMinuitMinimizer( - MaxIterations=1000, - Tolerance=0.01, - Algorithm="MIGRAD", - WithGradients=True, - FlatnessCheck=False, - IgnoreEDM=True, # Don't report convergence failures - CheckGradient=False, # Don't die on gradient errors - MinuitStrategy=0, # Don't try to check local curvature - MinuitPrintLevel=2 - ) - - coars_steps = dict(StepX=100.*I3Units.m, - StepY=100.*I3Units.m, - StepZ=100.*I3Units.m, - StepZenith=0., - StepAzimuth=0., - StepT=250.*I3Units.ns, - ShowerSpacing=0.*I3Units.m, - MuonSpacing=0, - Boundary=650*I3Units.m) - finer_steps = dict(StepX=2.*I3Units.m, - StepY=2.*I3Units.m, - StepZ=2.*I3Units.m, - StepZenith=0., - StepAzimuth=0., - StepT=5.*I3Units.ns, - ShowerSpacing=0*I3Units.m, - MuonSpacing=0, - Boundary=650*I3Units.m) - if seed is not None: - logger.debug('Updating StepXYZ') - self.UpdateStepXYZ(coars_steps, seed.dir, 150*I3Units.m) - self.UpdateStepXYZ(finer_steps, seed.dir, 3*I3Units.m) - tray.AddService('MuMillipedeParametrizationFactory', 'coarseSteps', **coars_steps) - - tray.AddService('I3BasicSeedServiceFactory', 'vetoseed', - FirstGuesses=[f'{cfg.OUTPUT_PARTICLE_NAME}', f'{cfg.OUTPUT_PARTICLE_NAME}_fallback'], - TimeShiftType='TNone', - PositionShiftType='None') - - tray.Add('I3SimpleFitter', - OutputName='MillipedeStarting1stPass', - SeedService='vetoseed', - Parametrization='coarseSteps', - LogLikelihood='millipedellh', - Minimizer='isimplex') - - def notify1(frame): - logger.debug(f"1st pass done! {datetime.datetime.now()}") - logger.debug(f"Seeded with: {frame[f'{cfg.OUTPUT_PARTICLE_NAME}']}") - logger.debug(f"MillipedeStarting1stPass: {frame['MillipedeStarting1stPass']}") - - tray.AddModule(notify1, "notify1") - - tray.AddService('MuMillipedeParametrizationFactory', 'fineSteps', **finer_steps) - - tray.AddService('I3BasicSeedServiceFactory', 'firstFitSeed', - FirstGuesses=['MillipedeStarting1stPass'], - TimeShiftType='TNone', - PositionShiftType='None') - - tray.Add('I3SimpleFitter', - SeedService='firstFitSeed', - OutputName='MillipedeStarting2ndPass_simplex', - Parametrization='fineSteps', - LogLikelihood='millipedellh', - Minimizer='isimplex') - - tray.AddService('I3BasicSeedServiceFactory', 'secondsimplexseed', - FirstGuesses=['MillipedeStarting2ndPass_simplex'], - TimeShiftType='TNone', - PositionShiftType='None') - - tray.Add('I3SimpleFitter', - SeedService='secondsimplexseed', - OutputName='MillipedeStarting2ndPass', - Parametrization='fineSteps', - LogLikelihood='millipedellh', - Minimizer='imigrad') - - def notify2(frame): - logger.debug(f"2nd pass done! {datetime.datetime.now()}") - logger.debug(f"MillipedeStarting2ndPass: {frame['MillipedeStarting2ndPass']}") - - tray.AddModule(notify2, "notify2") - - @staticmethod - def UpdateStepXYZ(the_steps, direction, uniform_step=15*I3Units.m): - the_steps['StepX'] = numpy.sqrt(1-direction.x**2)*uniform_step - the_steps['StepY'] = numpy.sqrt(1-direction.y**2)*uniform_step - the_steps['StepZ'] = numpy.sqrt(1-direction.z**2)*uniform_step - - @classmethod - def to_recopixelvariation(cls, frame: I3Frame, geometry: I3Frame) -> RecoPixelVariation: - # Calculate reco losses, based on load_scan_state() - reco_losses_inside, reco_losses_total = cls.get_reco_losses_inside( - p_frame=frame, g_frame=geometry, - ) - - if "MillipedeStarting2ndPass_millipedellh" not in frame: - llh = float("nan") - else: - llh = frame["MillipedeStarting2ndPass_millipedellh"].logl - return RecoPixelVariation( - nside=frame[cfg.I3FRAME_NSIDE].value, - pixel_id=frame[cfg.I3FRAME_PIXEL].value, - llh=llh, - reco_losses_inside=reco_losses_inside, - reco_losses_total=reco_losses_total, - posvar_id=frame[cfg.I3FRAME_POSVAR].value, - position=frame["MillipedeStarting2ndPass"].pos, - time=frame["MillipedeStarting2ndPass"].time, - energy=frame["MillipedeStarting2ndPass"].energy, - ) - - @staticmethod - def get_reco_losses_inside(p_frame: I3Frame, g_frame: I3Frame) -> Tuple[float, float]: - - if "MillipedeStarting2ndPass" not in p_frame: - return numpy.nan, numpy.nan - recoParticle = p_frame["MillipedeStarting2ndPass"] - - if "MillipedeStarting2ndPassParams" not in p_frame: - return numpy.nan, numpy.nan - - def getRecoLosses(vecParticles): - losses = [] - for p in vecParticles: - if not p.is_cascade: - continue - if p.energy == 0.: - continue - losses.append([p.time, p.energy]) - return losses - recoLosses = getRecoLosses(p_frame["MillipedeStarting2ndPassParams"]) - - - intersectionPoints = VHESelfVeto.IntersectionsWithInstrumentedVolume(g_frame["I3Geometry"], recoParticle) - intersectionTimes = [] - for intersectionPoint in intersectionPoints: - vecX = intersectionPoint.x - recoParticle.pos.x - vecY = intersectionPoint.y - recoParticle.pos.y - vecZ = intersectionPoint.z - recoParticle.pos.z - - prod = vecX*recoParticle.dir.x + vecY*recoParticle.dir.y + vecZ*recoParticle.dir.z - dist = numpy.sqrt(vecX**2 + vecY**2 + vecZ**2) - if prod < 0.: - dist *= -1. - intersectionTimes.append(dist/dataclasses.I3Constants.c + recoParticle.time) - - entryTime = None - exitTime = None - intersectionTimes = sorted(intersectionTimes) - if len(intersectionTimes) == 0: - return 0., 0. - - entryTime = intersectionTimes[0]-60.*I3Units.m/dataclasses.I3Constants.c - intersectionTimes = intersectionTimes[1:] - exitTime = intersectionTimes[-1]+60.*I3Units.m/dataclasses.I3Constants.c - intersectionTimes = intersectionTimes[:-1] - - totalRecoLosses = 0. - totalRecoLossesInside = 0. - for entry in recoLosses: - totalRecoLosses += entry[1] - if entryTime is not None and entry[0] < entryTime: - continue - if exitTime is not None and entry[0] > exitTime: - continue - totalRecoLossesInside += entry[1] - - return totalRecoLossesInside, totalRecoLosses - -RECO_CLASS: Final[type[RecoInterface]] = MillipedeWilks From 599d570c7bb5d4769bff1db89e25db95e2f71ace Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 11 Mar 2026 20:28:56 +0000 Subject: [PATCH 05/63] update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 20937de90..61a607eda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ ] dynamic = ["version"] # do not edit — autogenerated by wipac-dev-py-setup-action name = "skymap-scanner" # do not edit — autogenerated by wipac-dev-py-setup-action -requires-python = ">=3.10, <3.14" # do not edit — autogenerated by wipac-dev-py-setup-action +requires-python = ">=3.10, <3.15" # do not edit — autogenerated by wipac-dev-py-setup-action keywords = [ 'WIPAC', 'IceCube', From 97747b1660c5015637390fc1dcf8be6f4e95a01f Mon Sep 17 00:00:00 2001 From: Emma Tintinger <125337382+EmmaTintinger@users.noreply.github.com> Date: Wed, 11 Mar 2026 16:53:37 -0400 Subject: [PATCH 06/63] Merge main updates to monopod_wilks (#319) Update monopod_wilks branch to include updates to main --------- Co-authored-by: Ric Evans <19216225+ric-evans@users.noreply.github.com> Co-authored-by: ric-evans Co-authored-by: github-actions --- .github/workflows/publish.yml | 2 +- .github/workflows/tests.yml | 585 ++++++++++------------ Dockerfile | 11 +- pyproject.toml | 16 +- resources/build_apptainer_image.sh | 58 +++ resources/launch_scripts/launch_worker.sh | 102 +++- resources/launch_scripts/local_scan.py | 168 +++++-- skymap_scanner/server/__init__.py | 9 + skymap_scanner/server/utils.py | 1 + 9 files changed, 540 insertions(+), 412 deletions(-) create mode 100755 resources/build_apptainer_image.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0633daeac..5a82bcaef 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: image-publish: - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.13 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.20 permissions: # for GITHUB_TOKEN packages: write with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d1664351b..71326eeb6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,19 +16,35 @@ concurrency: env: - PY_COLORS: "1" - BOT_NAME: wipacdevbot - BOT_EMAIL: developers@icecube.wisc.edu + # pilot image tags — versioned! + _PILOT_IMAGE_FOR_DOCKER_SCANNER_CLIENT: "ghcr.io/observation-management-service/ewms-pilot:1.2.0-docker-tasks" + _PILOT_IMAGE_FOR_APPTAINER_SCANNER_CLIENT: "ghcr.io/observation-management-service/ewms-pilot:1.2.0" # + # url pointing to remote helper scripts — versioned! + CI_SCRIPT_URL_INSTALL_APPTAINER: https://raw.githubusercontent.com/WIPACrepo/wipac-dev-tools/refs/tags/v1.17.8/resources/apptainer/install-apptainer.sh + CI_SCRIPT_URL_DOOD_RUN: https://raw.githubusercontent.com/WIPACrepo/wipac-dev-tools/refs/tags/v1.17.8/resources/docker/run-docker-outside-of-docker.sh + CI_SCRIPT_URL_RUN_MQ_BROKER: https://raw.githubusercontent.com/Observation-Management-Service/ewms-pilot/refs/tags/v1.1.2/tests/integration/run-broker.sh + # + # used for/by mq broker script + RABBITMQ_IMAGE_TAG: bitnamilegacy/rabbitmq:3.13.5 + MQ_BROKER: "scanner_broker" + # + # use by local_scan.py CI_LOCAL_SCAN_TAIL: 20 - CI_DOCKER_IMAGE_TAG: icecube/skymap_scanner:local # + _SCANNER_IMAGE_DOCKER: icecube/skymap_scanner:local + # + # a special network set up so docker scanner clients (inside of docker pilots) can talk to the mq broker + DOOD_NETWORK: mynet + # + # used for configuring the scanner runtime CI_TEST_RUN_STDOUT_STDERR_DIR: /home/runner/work/skymap_scanner/testrun_outputs N_WORKERS: 2 REALTIME_EVENTS_DIR: /home/runner/work/skymap_scanner/skymap_scanner/tests/data/realtime_events CI_SKYSCAN_CACHE_DIR: /home/runner/work/skymap_scanner/skymap_scanner/cache CI_SKYSCAN_OUTPUT_DIR: /home/runner/work/skymap_scanner/skymap_scanner/output CI_SKYSCAN_DEBUG_DIR: /home/runner/work/skymap_scanner/skymap_scanner/debug + # # see source tests/env-vars.sh @@ -43,7 +59,75 @@ jobs: with: ref: ${{ github.sha }} # lock to triggered commit (github.ref is dynamic) - id: versions - uses: WIPACrepo/wipac-dev-py-versions-action@v2.7 + uses: WIPACrepo/wipac-dev-py-versions-action@v2.8 + + _yaml_templates: + if: ${{ false }} # never runs; holds YAML anchors only — bit of a hack, but reduces code duplication + runs-on: ubuntu-latest + steps: + - &df_disk_space + name: "— Disk Space —" + if: always() + run: df -h / + + - &free_disk_space + uses: Jayllyz/free-disk-space@3bda29d61d3f1fa7bf46c5a9a11f22dd20af07c9 + with: + docker-images: false + + - &dump_output_central_server + name: "dump output — central server" + if: always() + run: | + set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + cat $CI_TEST_RUN_STDOUT_STDERR_DIR/server.out + + - &dump_output_worker_1_pilot + name: "dump output — worker #1 — pilot" + if: always() + run: | + set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot.out | cat + + - &dump_output_worker_1_clients + name: "dump output — worker #1 — clients (icetray pixel reconstructions)" + if: always() + run: | + set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat + echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* + + - &dump_output_worker_2_pilot + name: "dump output — worker #2 — pilot" + if: always() && env.N_WORKERS == '2' + run: | + set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot.out | cat + + - &dump_output_worker_2_clients + name: "dump output — worker #2 — clients (icetray pixel reconstructions)" + if: always() && env.N_WORKERS == '2' + run: | + set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat + echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* + + - &dump_rabbitmq_diagnostics + name: RabbitMQ diagnostics + if: always() + run: | + set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + echo "=== docker logs (rabbitmq) ===" + docker logs "$MQ_BROKER" || true + echo "=== rabbitmqctl status ===" + docker exec "$MQ_BROKER" rabbitmqctl status || true + echo "=== rabbitmq-diagnostics memory ===" + docker exec "$MQ_BROKER" rabbitmq-diagnostics memory || true + echo "=== rabbitmq-diagnostics environment ===" + docker exec "$MQ_BROKER" rabbitmq-diagnostics environment || true + echo "=== rabbitmq-diagnostics alarms ===" + docker exec "$MQ_BROKER" rabbitmq-diagnostics alarms || true + ############################################################################# # LINTERS @@ -53,7 +137,8 @@ jobs: needs: [ py-versions ] runs-on: ubuntu-latest strategy: - fail-fast: false + # don't fail fast on default branch — so we can rerun just the one job in case of transient failure + fail-fast: ${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref }} matrix: py3: ${{ fromJSON(needs.py-versions.outputs.matrix) }} steps: @@ -71,7 +156,8 @@ jobs: needs: [ py-versions ] runs-on: ubuntu-latest strategy: - fail-fast: false + # don't fail fast on default branch — so we can rerun just the one job in case of transient failure + fail-fast: ${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref }} matrix: py3: ${{ fromJSON(needs.py-versions.outputs.matrix) }} steps: @@ -90,13 +176,14 @@ jobs: ############################################################################# py-setup: + if: ${{ github.actor != 'dependabot[bot]' }} # dependabot cannot access PAT runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} ref: ${{ github.ref }} # dont lock to sha (action needs to push) - - uses: WIPACrepo/wipac-dev-py-setup-action@v5.3 + - uses: WIPACrepo/wipac-dev-py-setup-action@v5.7 with: mode: PACKAGING python_min: '3.10' @@ -107,18 +194,30 @@ jobs: needs: [ flake8 ] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + # optimization: ask the action if it would proceed before building our expensive images + - uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 + id: pydep-precheck + with: + only_precheck: true + + # pre-check passed, so proceed... + - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} + uses: actions/checkout@v5 with: ref: ${{ github.sha }} # lock to triggered commit (github.ref is dynamic) fetch-depth: 0 # setuptools-scm needs to access git tags - - uses: docker/setup-buildx-action@v3 - - uses: docker/build-push-action@v6 + - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} + uses: docker/setup-buildx-action@v3 + - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} + uses: docker/build-push-action@v6 with: context: . file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true - - uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.1 + build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files + - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} + uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 ############################################################################# @@ -138,11 +237,11 @@ jobs: with: context: . file: ./Dockerfile - tags: ${{ env.CI_DOCKER_IMAGE_TAG }} - load: true + tags: ${{ env._SCANNER_IMAGE_DOCKER }} + load: false # for checking if this builds, no loading needed test-run-dummy: - # needs: [ flake8 ] # remove so this starts up asap w/ priority + needs: [ flake8 ] runs-on: ubuntu-latest strategy: fail-fast: false @@ -156,39 +255,16 @@ jobs: "apptainer", ] env: - SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS: user1@localhost/test - SKYSCAN_MQ_TOCLIENT_AUTH_TOKEN: password # using this would override password in address - SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS: user1@localhost/test - SKYSCAN_MQ_FROMCLIENT_AUTH_TOKEN: password # using this would override password in address + _RECO_ALGO: "${{ matrix.reco_algo }}" + _CI_SCANNER_CONTAINER_PLATFORM: "${{ matrix.container_platform }}" + _NSIDES: "1:0 2:12 4:12" EWMS_PILOT_STOP_LISTENING_ON_TASK_ERROR: True # one crash per pilot is enough EWMS_PILOT_TASK_TIMEOUT: 15 - services: - rabbitmq: - # see image.tag -> https://github.com/Observation-Management-Service/path-kubernetes/blob/main/helm-values-rabbitmq-bitnami.yaml (see https://artifacthub.io/packages/helm/bitnami/rabbitmq/11.14.3) - image: bitnamilegacy/rabbitmq:3.11.15-debian-11-r0 - env: - RABBITMQ_USERNAME: user1 - RABBITMQ_PASSWORD: password - RABBITMQ_VHOST: test - BITNAMI_DEBUG: true - # Use the Bitnami-specific absolute limit var: - RABBITMQ_DISK_FREE_ABSOLUTE_LIMIT: "1MB" - # (Optional) leave memory watermark alone or keep it generous: - RABBITMQ_VM_MEMORY_HIGH_WATERMARK: "0.9" - # Note: `--network` option is not supported. - options: >- - --name rabbitmq - --health-cmd "rabbitmqctl node_health_check" - --health-interval 5s - --health-timeout 5s - --health-retries 10 - ports: - - 5672:5672 - - 15672:15672 steps: - - uses: Jayllyz/free-disk-space@3bda29d61d3f1fa7bf46c5a9a11f22dd20af07c9 # until https://github.com/jlumbroso/free-disk-space/pull/26 # need space for mq broker and image - with: - docker-images: false + - *df_disk_space + - *free_disk_space + - *df_disk_space + - uses: actions/checkout@v5 with: ref: ${{ github.sha }} # lock to triggered commit (github.ref is dynamic) @@ -198,116 +274,64 @@ jobs: with: context: . file: ./Dockerfile - tags: ${{ env.CI_DOCKER_IMAGE_TAG }} + tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} - name: install apptainer + name: "apptainer only: install apptainer" run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - set -x - - # https://github.com/apptainer/apptainer/blob/main/INSTALL.md#installing-apptainer - # Ensure repositories are up-to-date - sudo apt-get update - # Install debian packages for dependencies - sudo apt-get install -y \ - build-essential \ - libseccomp-dev \ - pkg-config \ - uidmap \ - squashfs-tools \ - fakeroot \ - cryptsetup \ - tzdata \ - dh-apparmor \ - curl wget git - # Clone the repo - git clone https://github.com/apptainer/apptainer.git - cd apptainer - git checkout v1.3.2 - # Compiling Apptainer - ./mconfig - cd $(/bin/pwd)/builddir - make - sudo make install - apptainer --version - - # https://github.com/apptainer/apptainer/blob/main/INSTALL.md#apparmor-profile-ubuntu-2310 - sudo tee /etc/apparmor.d/apptainer << 'EOF' - # Permit unprivileged user namespace creation for apptainer starter - abi , - include - profile apptainer /usr/local/libexec/apptainer/bin/starter{,-suid} - flags=(unconfined) { - userns, - # Site-specific additions and overrides. See local/README for details. - include if exists - } - EOF - sudo systemctl reload apparmor - - # Install squashfuse in order to run .sif - # without squashfuse, .sif can't be run directly and needs to be converted - # to a sandbox dir, 1 for each instance - sudo apt-get update - sudo apt-get install -y squashfuse + curl -fsSL "$CI_SCRIPT_URL_INSTALL_APPTAINER" -o install-apptainer.sh && chmod +x install-apptainer.sh + ./install-apptainer.sh --sif no # we will run sandboxes (dirs), not .sif files - if: ${{ matrix.container_platform == 'apptainer' }} - name: build apptainer (.sif) image + name: "apptainer only: build apptainer image" env: # keep caches off $HOME and easy to delete APPTAINER_CACHEDIR: ${{ runner.temp }}/apptainer-cache APPTAINER_TMPDIR: ${{ runner.temp }} - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - - apptainer build skymap_scanner.sif docker-daemon://$CI_DOCKER_IMAGE_TAG - ls -lh skymap_scanner.sif - # drop apptainer caches - echo "clearing apptainer caches..." - du -sh "$APPTAINER_CACHEDIR" || true - rm -rf "$APPTAINER_CACHEDIR" || true - - # Free docker stuff now that SIF is built - echo "clearing docker things..." - BEFORE="$(df -B1 --output=avail / | tail -1)" - # -- docker layers - docker ps -a --filter "ancestor=$CI_DOCKER_IMAGE_TAG" -q | xargs -r docker rm -f - docker rmi -f "$CI_DOCKER_IMAGE_TAG" || true - # -- prune buildkit + volume - docker ps -aq --filter "label=name=buildx_buildkit" | xargs -r docker rm -f || true - docker ps -aq --filter "ancestor=moby/buildkit:buildx-stable-1" | xargs -r docker rm -f || true - docker buildx ls | awk 'NR>1{gsub(/\*$/,"",$1); if($1!="default" && $1!="") print $1}' | xargs -r -n1 docker buildx rm -f || true - docker builder prune -af || true - docker system prune -af --volumes || true - docker volume ls -q --filter 'name=buildx_buildkit_.*_state' | xargs -r docker volume rm -f || true - # -- report - AFTER="$(df -B1 --output=avail / | tail -1)" - DELTA="$((AFTER - BEFORE))" - GIB="$(awk -v b="$DELTA" 'BEGIN{printf "%.2f", b/1024/1024/1024}')" - MIB="$(awk -v b="$DELTA" 'BEGIN{printf "%.0f", b/1024/1024}')" - echo "Freed: ${GIB} GiB (${MIB} MiB)" + run: ./resources/build_apptainer_image.sh - name: run timeout-minutes: 15 # on average ~9min run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" set -x + + # step 1: CREATE DOCKER NETWORK + if [[ "${{ matrix.container_platform }}" == "docker" ]]; then + docker network create "$DOOD_NETWORK" + fi + + # step 2: START MQ BROKER — this is always in a docker container, even in 'apptainer' tests + curl -fsSL "$CI_SCRIPT_URL_RUN_MQ_BROKER" -o run-mq-broker.sh && chmod +x run-mq-broker.sh + if [[ "${{ matrix.container_platform }}" == "docker" ]]; then + # USE THE DOCKER NETWORK CREATED IN PREVIOUS STEP + source ./run-mq-broker.sh "rabbitmq" "$MQ_BROKER" "--network=$DOOD_NETWORK" + export SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS="$MQ_BROKER" + export SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS="$MQ_BROKER" + elif [[ "${{ matrix.container_platform }}" == "apptainer" ]]; then + # USE THE HOST NETWORK SINCE APPTAINER DOESN'T HAVE AS ISOLATED NETWORKING AS DOCKER DOES + source ./run-mq-broker.sh "rabbitmq" "$MQ_BROKER" "--network=host" + export SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS="localhost" # localhost b/c using --network=host + export SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS="localhost" # localhost b/c using --network=host + else + echo "::error::unknown container_platform: ${{ matrix.container_platform }}" + exit 2 + fi + docker ps + + # step 3: SET ENV VARS — these require bash to set, see this job's and yaml's `env` blocks for others source tests/env-vars.sh - export _RECO_ALGO="${{ matrix.reco_algo }}" export _EVENTS_FILE=$(realpath $REALTIME_EVENTS_DIR/hese_event_01.json) - export _NSIDES="1:0 2:12 4:12" - - # set special env vars if [ "${{ matrix.container_platform }}" == "apptainer" ]; then - export _RUN_THIS_SIF_IMAGE="$(realpath skymap_scanner.sif)" - export _EWMS_PILOT_APPTAINER_IMAGE_DIRECTORY_MUST_BE_PRESENT=False + export _SCANNER_IMAGE_APPTAINER="$(realpath skymap_scanner.sandbox)" fi if [ "${{ matrix.reco_algo }}" == "crash_dummy" ]; then export _SKYSCAN_CI_CRASH_DUMMY_PROBABILITY=0.75 fi - # run! + # step 4: RUN! cd ./resources/launch_scripts if [ "${{ matrix.reco_algo }}" == "crash_dummy" ]; then set +e # allow `||` to supress first non-zero exit @@ -318,24 +342,23 @@ jobs: fi - if: ${{ matrix.reco_algo == 'crash_dummy' }} - name: look at stderrfiles + name: "crash_dummy only: see the crashes happened as expected" run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" set -x # check for fails/errors - error_type_1='intentional crash-dummy error' error_type_2='\[Timeout-Error\] timed out after' pattern="$error_type_1|$error_type_2" - if find "$CI_TEST_RUN_STDOUT_STDERR_DIR/worker-"*/pilot.out -type f -exec grep -qE "$pattern" {} +; then echo "Match(es) found: PilotSubprocessError and/or TimeoutError occurred." else echo "::error::Could not find the expected error(s) in worker/pilot outputs." exit 1 fi + - if: ${{ matrix.reco_algo == 'dummy' }} - name: look at results file (.npz) + name: "dummy only: dump results file (.npz)" run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" ls . @@ -343,55 +366,23 @@ jobs: outfile=$(ls -d $CI_SKYSCAN_OUTPUT_DIR/*.npz) echo $outfile - - name: dump output -- central server stdout/stderr - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - cat $CI_TEST_RUN_STDOUT_STDERR_DIR/server.out - - name: dump output -- worker pilot \#1 stdout/stderr - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot.out | cat - - name: dump output -- worker clients / reco-icetray instances \#1 stdouts/stderrs - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat - echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* - - name: dump output -- worker pilot \#2 stdout/stderr - if: always() && env.N_WORKERS == '2' - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot.out | cat - - name: dump output -- worker clients / reco-icetray instances \#2 stdouts/stderrs - if: always() && env.N_WORKERS == '2' - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat - echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* + - *dump_output_central_server + - *dump_output_worker_1_pilot + - *dump_output_worker_1_clients + - *dump_output_worker_2_pilot + - *dump_output_worker_2_clients + - *dump_rabbitmq_diagnostics - - name: RabbitMQ diagnostics - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - echo "=== docker logs (rabbitmq) ===" - docker logs rabbitmq || true - echo "=== rabbitmqctl status ===" - docker exec rabbitmq rabbitmqctl status || true - echo "=== rabbitmq-diagnostics memory ===" - docker exec rabbitmq rabbitmq-diagnostics memory || true - echo "=== rabbitmq-diagnostics environment ===" - docker exec rabbitmq rabbitmq-diagnostics environment || true - echo "=== rabbitmq-diagnostics alarms ===" - docker exec rabbitmq rabbitmq-diagnostics alarms || true + - *df_disk_space test-run-nsides-thresholds-dummy: + if: ${{ github.actor != 'dependabot[bot]' }} # don't run expensive job when not needed needs: [ flake8 ] runs-on: ubuntu-latest strategy: - fail-fast: false + # don't fail fast on default branch — so we can rerun just the one job in case of transient failure + fail-fast: ${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref }} matrix: nsides: [ "1:0", @@ -403,33 +394,15 @@ jobs: 0.65, ] env: - SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS: user1@localhost/test - SKYSCAN_MQ_TOCLIENT_AUTH_TOKEN: password # using this would override password in address - SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS: user1@localhost/test - SKYSCAN_MQ_FROMCLIENT_AUTH_TOKEN: password # using this would override password in address - services: - rabbitmq: - # see image.tag -> https://github.com/Observation-Management-Service/path-kubernetes/blob/main/helm-values-rabbitmq-bitnami.yaml (see https://artifacthub.io/packages/helm/bitnami/rabbitmq/11.14.3) - image: bitnamilegacy/rabbitmq:3.11.15-debian-11-r0 - env: - RABBITMQ_USERNAME: user1 - RABBITMQ_PASSWORD: password - RABBITMQ_VHOST: test - BITNAMI_DEBUG: true - # Note: `--network` option is not supported. - options: >- - --name rabbitmq - --health-cmd "rabbitmqctl node_health_check" - --health-interval 5s - --health-timeout 5s - --health-retries 10 - ports: - - 5672:5672 - - 15672:15672 + _CI_SCANNER_CONTAINER_PLATFORM: docker + _RECO_ALGO: dummy + _NSIDES: "${{ matrix.nsides }}" + _PREDICTIVE_SCANNING_THRESHOLD: ${{ matrix.predictive_scanning_threshold }} steps: - - uses: Jayllyz/free-disk-space@3bda29d61d3f1fa7bf46c5a9a11f22dd20af07c9 # until https://github.com/jlumbroso/free-disk-space/pull/26 # need space for mq broker and image - with: - docker-images: false + - *df_disk_space + - *free_disk_space + - *df_disk_space + - uses: actions/checkout@v5 with: ref: ${{ github.sha }} # lock to triggered commit (github.ref is dynamic) @@ -439,19 +412,30 @@ jobs: with: context: . file: ./Dockerfile - tags: ${{ env.CI_DOCKER_IMAGE_TAG }} + tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files + - name: run timeout-minutes: 12 # on average max~=8.5min run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + + # step 1: CREATE DOCKER NETWORK + docker network create "$DOOD_NETWORK" + + # step 2: START MQ BROKER — using the docker network created above + curl -fsSL "$CI_SCRIPT_URL_RUN_MQ_BROKER" -o run-mq-broker.sh && chmod +x run-mq-broker.sh + source ./run-mq-broker.sh "rabbitmq" "$MQ_BROKER" "--network=$DOOD_NETWORK" + export SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS="$MQ_BROKER" + export SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS="$MQ_BROKER" + + # step 3: SET ENV VARS — these require bash to set, see this job's and yaml's `env` blocks for others set -x source tests/env-vars.sh - export _RECO_ALGO=dummy export _EVENTS_FILE=$(realpath $REALTIME_EVENTS_DIR/hese_event_01.json) - export _NSIDES="${{ matrix.nsides }}" - export _PREDICTIVE_SCANNING_THRESHOLD=${{ matrix.predictive_scanning_threshold }} + # step 4: RUN! cd ./resources/launch_scripts python3 local_scan.py $N_WORKERS $CI_TEST_RUN_STDOUT_STDERR_DIR @@ -471,46 +455,22 @@ jobs: assert len(pydict) == nsides.count(":") ' - - name: dump output -- central server stdout/stderr - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - cat $CI_TEST_RUN_STDOUT_STDERR_DIR/server.out - - name: dump output -- worker pilot \#1 stdout/stderr - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot.out | cat - - name: dump output -- worker clients / reco-icetray instances \#1 stdouts/stderrs - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat - echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* - - name: dump output -- worker pilot \#2 stdout/stderr - if: always() && env.N_WORKERS == '2' - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot.out | cat - - name: dump output -- worker clients / reco-icetray instances \#2 stdouts/stderrs - if: always() && env.N_WORKERS == '2' - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat - echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* - - - name: rabbitmq logs - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - docker logs rabbitmq + - *dump_output_central_server + - *dump_output_worker_1_pilot + - *dump_output_worker_1_clients + - *dump_output_worker_2_pilot + - *dump_output_worker_2_clients + - *dump_rabbitmq_diagnostics + - *df_disk_space test-run-realistic: + if: ${{ github.actor != 'dependabot[bot]' }} # don't run expensive job when not needed needs: [ flake8 ] runs-on: ubuntu-latest strategy: - fail-fast: false + # don't fail fast on default branch — so we can rerun just the one job in case of transient failure + fail-fast: ${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref }} matrix: reco_algo: [ millipede_original, @@ -531,33 +491,14 @@ jobs: - reco_algo: splinempe_pointed eventfile: hese_event_01.json env: - SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS: user1@localhost/test - SKYSCAN_MQ_TOCLIENT_AUTH_TOKEN: password # using this would override password in address - SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS: user1@localhost/test - SKYSCAN_MQ_FROMCLIENT_AUTH_TOKEN: password # using this would override password in address - services: - rabbitmq: - # see image.tag -> https://github.com/Observation-Management-Service/path-kubernetes/blob/main/helm-values-rabbitmq-bitnami.yaml (see https://artifacthub.io/packages/helm/bitnami/rabbitmq/11.14.3) - image: bitnamilegacy/rabbitmq:3.11.15-debian-11-r0 - env: - RABBITMQ_USERNAME: user1 - RABBITMQ_PASSWORD: password - RABBITMQ_VHOST: test - BITNAMI_DEBUG: true - # Note: `--network` option is not supported. - options: >- - --name rabbitmq - --health-cmd "rabbitmqctl node_health_check" - --health-interval 5s - --health-timeout 5s - --health-retries 10 - ports: - - 5672:5672 - - 15672:15672 + _RECO_ALGO: "${{ matrix.reco_algo }}" + _CI_SCANNER_CONTAINER_PLATFORM: "docker" + _NSIDES: "1:0" steps: - - uses: Jayllyz/free-disk-space@3bda29d61d3f1fa7bf46c5a9a11f22dd20af07c9 # until https://github.com/jlumbroso/free-disk-space/pull/26 # need space for mq broker and image - with: - docker-images: false + - *df_disk_space + - *free_disk_space + - *df_disk_space + - uses: actions/checkout@v5 with: ref: ${{ github.sha }} # lock to triggered commit (github.ref is dynamic) @@ -567,21 +508,28 @@ jobs: with: context: . file: ./Dockerfile - tags: ${{ env.CI_DOCKER_IMAGE_TAG }} + tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - name: run timeout-minutes: 55 # on average max~=35min run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - set -x - lscpu - python3 --version + + # step 1: CREATE DOCKER NETWORK + docker network create "$DOOD_NETWORK" + + # step 2: START MQ BROKER — using the docker network created above + curl -fsSL "$CI_SCRIPT_URL_RUN_MQ_BROKER" -o run-mq-broker.sh && chmod +x run-mq-broker.sh + source ./run-mq-broker.sh "rabbitmq" "$MQ_BROKER" "--network=$DOOD_NETWORK" + export SKYSCAN_MQ_TOCLIENT_BROKER_ADDRESS="$MQ_BROKER" + export SKYSCAN_MQ_FROMCLIENT_BROKER_ADDRESS="$MQ_BROKER" + + # setp 3: SET ENV VARS — these require bash to set, see this job's and yaml's `env` blocks for others source tests/env-vars.sh - export _RECO_ALGO=${{ matrix.reco_algo }} export _EVENTS_FILE=$(realpath $REALTIME_EVENTS_DIR/${{ matrix.eventfile }}) - export _NSIDES="1:0" + # step 4: RUN! cd ./resources/launch_scripts python3 local_scan.py $N_WORKERS $CI_TEST_RUN_STDOUT_STDERR_DIR @@ -594,8 +542,8 @@ jobs: echo $outfile cat $outfile pip install . # don't need icecube, so no docker container needed - - + + # -------------------------------------------------------------------- # splinempe: special handling + gh-issue reminder timer if [[ "${{ matrix.reco_algo }}" == "splinempe" ]]; then @@ -605,60 +553,34 @@ jobs: --assert \ --compare-different-versions-ok \ || (cat $(ls *.diff.json) && false) - + # put the timer after, so at least we know if the test passes - start_time="2025-03-18" # Set the starting date - end_time=$(date -d "$start_time +6 months" +%s) + start_time="2025-11-7" # Set the starting date + end_time=$(date -d "$start_time +2 months" +%s) current_time=$(date +%s) if (( current_time >= end_time )); then - echo "::error::it's been six months, how's https://github.com/icecube/skymap_scanner/issues/242 going? Are version-0 result files still being used for splinempe?" + echo "::error::alarm went off, how's https://github.com/icecube/skymap_scanner/issues/242 going? Are version-0 result files still being used for splinempe?" exit 1 fi - + exit 0 fi # -------------------------------------------------------------------- - - + + python tests/compare_scan_results.py \ --actual $outfile \ --expected tests/data/results_json/${{ matrix.reco_algo }}/$(basename $outfile) \ --assert \ || (cat $(ls *.diff.json) && false) - - name: dump output -- central server stdout/stderr - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - cat $CI_TEST_RUN_STDOUT_STDERR_DIR/server.out - - name: dump output -- worker pilot \#1 stdout/stderr - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot.out | cat - - name: dump output -- worker clients / reco-icetray instances \#1 stdouts/stderrs - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat - echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-1/pilot-* - - name: dump output -- worker pilot \#2 stdout/stderr - if: always() && env.N_WORKERS == '2' - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - more $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot.out | cat - - name: dump output -- worker clients / reco-icetray instances \#2 stdouts/stderrs - if: always() && env.N_WORKERS == '2' - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - find $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* -name "stderrfile" -o -name "stdoutfile" | xargs more | cat - echo "::::::::::::::" && tree $CI_TEST_RUN_STDOUT_STDERR_DIR/worker-2/pilot-* - - - name: rabbitmq logs - if: always() - run: | - set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - docker logs rabbitmq + - *dump_output_central_server + - *dump_output_worker_1_pilot + - *dump_output_worker_1_clients + - *dump_output_worker_2_pilot + - *dump_output_worker_2_clients + - *dump_rabbitmq_diagnostics + - *df_disk_space - name: Upload results as artifacts if: always() @@ -666,7 +588,7 @@ jobs: with: name: test-run-realistic-${{ matrix.reco_algo }}-${{ matrix.eventfile }}-results path: | - ${{ env.CI_SKYSCAN_OUTPUT_DIR }}/** + ${{ env.CI_SKYSCAN_OUTPUT_DIR }}** *.diff.json if-no-files-found: warn retention-days: 7 @@ -685,17 +607,21 @@ jobs: with: context: . file: ./Dockerfile - tags: ${{ env.CI_DOCKER_IMAGE_TAG }} + tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + - name: run run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" - source tests/env-vars.sh + # SET ENV VARS — these require bash to set, see this job's and yaml's `env` blocks for others + source tests/env-vars.sh + + # RUN! docker run --rm -i \ --mount type=bind,source=$(readlink -f tests/),target=/local/tests/ \ $(env | grep -E '^(SKYSCAN_|_SKYSCAN_)' | cut -d'=' -f1 | sed 's/^/--env /') \ - $CI_DOCKER_IMAGE_TAG \ + $_SCANNER_IMAGE_DOCKER \ python /local/tests/file_staging.py @@ -703,7 +629,8 @@ jobs: needs: [ flake8 ] runs-on: ubuntu-latest strategy: - fail-fast: false + # don't fail fast on default branch — so we can rerun just the one job in case of transient failure + fail-fast: ${{ format('refs/heads/{0}', github.event.repository.default_branch) != github.ref }} matrix: dir: [ "BRONZE", @@ -733,22 +660,26 @@ jobs: with: context: . file: ./Dockerfile - tags: ${{ env.CI_DOCKER_IMAGE_TAG }} + tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + - name: run timeout-minutes: 10 # on average max~=5min run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" set -e - source tests/env-vars.sh - # run reco directly + # SET ENV VARS — these require bash to set, see this job's and yaml's `env` blocks for others + source tests/env-vars.sh + + # RUN! + # -> run reco directly without an ewms pilot wrapper docker run --network="host" --rm -i \ --shm-size=6gb \ --mount type=bind,source=$(readlink -f tests/),target=/local/tests/ \ --env PY_COLORS=1 \ $(env | grep -E '^(SKYSCAN_|_SKYSCAN_)' | cut -d'=' -f1 | sed 's/^/--env /') \ - $CI_DOCKER_IMAGE_TAG \ + $_SCANNER_IMAGE_DOCKER \ python -m skymap_scanner.client \ --infile /local/tests/data/reco_pixel_single/${{ matrix.reco_algo }}/${{ matrix.dir }}/in.json \ --client-startup-json /local/tests/data/reco_pixel_single/${{ matrix.reco_algo }}/${{ matrix.dir }}/startup.json \ @@ -758,7 +689,7 @@ jobs: run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" source tests/env-vars.sh - + ls tests/data/reco_pixel_pkls/${{ matrix.reco_algo }}/${{ matrix.dir }} ls tests/data/reco_pixel_single/${{ matrix.reco_algo }}/${{ matrix.dir }} @@ -768,7 +699,7 @@ jobs: --mount type=bind,source=$(readlink -f tests/),target=/local/tests/ \ --env PY_COLORS=1 \ $(env | grep -E '^(SKYSCAN_|_SKYSCAN_)' | cut -d'=' -f1 | sed 's/^/--env /') \ - $CI_DOCKER_IMAGE_TAG \ + $_SCANNER_IMAGE_DOCKER \ python /local/tests/compare_reco_pixel_single.py \ --actual /local/tests/data/reco_pixel_single/${{ matrix.reco_algo }}/${{ matrix.dir }}/out-actual.json \ --expected /local/tests/data/reco_pixel_single/${{ matrix.reco_algo }}/${{ matrix.dir }}/out.json \ @@ -808,7 +739,7 @@ jobs: test-run-realistic, test-run-single-pixel, ] - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/tag-and-release.yml@v1.13 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/tag-and-release.yml@v1.20 permissions: # for GITHUB_TOKEN contents: write with: diff --git a/Dockerfile b/Dockerfile index 17ad28e9a..34bd38d81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,15 @@ ARG ICETRAY_VERSION=v1.15.3-ubuntu22.04 FROM icecube/icetray:icetray-devel-v1.15.3_pr4012-ubuntu22.04 AS prod # gcd files -RUN mkdir -p /opt/i3-data/baseline_gcds && \ - wget -nv -N -t 5 -P /opt/i3-data/baseline_gcds -r -l 1 -A *.i3* -nd http://prod-exe.icecube.wisc.edu/baseline_gcds/ && \ +# -- toggle GCD download at build time (1=download, 0=skip) +ARG INCLUDE_GCD=1 +RUN set -eux; \ + mkdir -p /opt/i3-data/baseline_gcds; \ + if [ "${INCLUDE_GCD}" = "1" ]; then \ + wget -nv -N -t 5 -P /opt/i3-data/baseline_gcds -r -l 1 -A '*.i3*' -nd http://prod-exe.icecube.wisc.edu/baseline_gcds/; \ + else \ + echo "Skipping baseline_gcds download (INCLUDE_GCD=${INCLUDE_GCD})"; \ + fi; \ chmod -R u+rwX,go+rX,go-w /opt/i3-data/baseline_gcds # diff --git a/pyproject.toml b/pyproject.toml index 61a607eda..ca3036dc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,12 +51,18 @@ mypy = [ 'oms-mqclient[rabbitmq]', ] # do not edit — autogenerated by wipac-dev-py-setup-action +[tool.setuptools] +packages = [ + 'skymap_scanner', + 'skymap_scanner.client', + 'skymap_scanner.recos', + 'skymap_scanner.recos.common', + 'skymap_scanner.server', + 'skymap_scanner.utils', +] # do not edit — autogenerated by wipac-dev-py-setup-action + [tool.setuptools.package-data] "*" = ["py.typed"] # do not edit — autogenerated by wipac-dev-py-setup-action -[tool.setuptools.packages.find] # do not edit — autogenerated by wipac-dev-py-setup-action -namespaces = false -exclude = ["test", "tests", "doc", "docs", "resource", "resources", "example", "examples"] - [tool.setuptools_scm] # do not edit — autogenerated by wipac-dev-py-setup-action -fallback_version = "UNTAGGED" # only used when SCM metadata is completely unavailable (e.g., no '.git/'). NOTE: in shallow clones without git tags, setuptools-scm will still infer a commit-based version (like '0.1.dev1+g79c9b4b'); this value will NOT be used in that case. +fallback_version = "CANNOT_BUILD_WITHOUT_GIT_DIR" # used when SCM metadata is missing (no '.git/') — if '.git/' exists but does not contain tags, a commit-based version (like '0.1.dev1+g79c9b4b') will be used instead. diff --git a/resources/build_apptainer_image.sh b/resources/build_apptainer_image.sh new file mode 100755 index 000000000..db0994ad4 --- /dev/null +++ b/resources/build_apptainer_image.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -euo pipefail +echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" + +######################################################################## +# Require environment variables +######################################################################## +if [[ -z "${_SCANNER_IMAGE_DOCKER:-}" ]]; then + echo "::error:: _SCANNER_IMAGE_DOCKER must be set" + exit 1 +fi +if [[ -z "${APPTAINER_CACHEDIR:-}" ]]; then + echo "::error:: APPTAINER_CACHEDIR must be set" + exit 1 +fi + +APPTAINER_SANDBOX_DIR="skymap_scanner.sandbox" + +######################################################################## +# Build sandbox image +######################################################################## +echo "building apptainer sandbox at: ${APPTAINER_SANDBOX_DIR}" +# ensure a clean target (apptainer refuses to overwrite without --force) +rm -rf "${APPTAINER_SANDBOX_DIR}" || true +apptainer build --sandbox --force "${APPTAINER_SANDBOX_DIR}" docker-daemon://"$_SCANNER_IMAGE_DOCKER" +du -sh "${APPTAINER_SANDBOX_DIR}" || ls -lh "${APPTAINER_SANDBOX_DIR}" + +######################################################################## +# Drop apptainer caches +######################################################################## +echo "clearing apptainer caches..." +du -sh "$APPTAINER_CACHEDIR" || true +rm -rf "$APPTAINER_CACHEDIR" || true + +######################################################################## +# Free docker stuff now that sandbox is built +######################################################################## +echo "clearing docker things..." +BEFORE="$(df -B1 --output=avail / | tail -1)" + +# -- docker layers +docker ps -a --filter "ancestor=$_SCANNER_IMAGE_DOCKER" -q | xargs -r docker rm -f +docker rmi -f "$_SCANNER_IMAGE_DOCKER" || true + +# -- prune buildkit + volume +docker ps -aq --filter "label=name=buildx_buildkit" | xargs -r docker rm -f || true +docker ps -aq --filter "ancestor=moby/buildkit:buildx-stable-1" | xargs -r docker rm -f || true +docker buildx ls | awk 'NR>1{gsub(/\*$/,"",$1); if($1!="default" && $1!="") print $1}' | xargs -r -n1 docker buildx rm -f || true +docker builder prune -af || true +docker system prune -af --volumes || true +docker volume ls -q --filter 'name=buildx_buildkit_.*_state' | xargs -r docker volume rm -f || true + +# -- report +AFTER="$(df -B1 --output=avail / | tail -1)" +DELTA="$((AFTER - BEFORE))" +GIB="$(awk -v b="$DELTA" 'BEGIN{printf "%.2f", b/1024/1024/1024}')" +MIB="$(awk -v b="$DELTA" 'BEGIN{printf "%.0f", b/1024/1024}')" +echo "Freed: ${GIB} GiB (${MIB} MiB)" diff --git a/resources/launch_scripts/launch_worker.sh b/resources/launch_scripts/launch_worker.sh index 6ddd33dbc..6d60662c3 100755 --- a/resources/launch_scripts/launch_worker.sh +++ b/resources/launch_scripts/launch_worker.sh @@ -1,19 +1,26 @@ #!/bin/bash set -euo pipefail -set -ex ######################################################################## # # Launch a Skymap Scanner worker +# Uses '_CI_SCANNER_CONTAINER_PLATFORM' to control the mode: # -# Run worker on ewms pilot +# - Docker mode: +# runs the pilot image (docker) that runs Docker scanner clients +# - Apptainer mode: +# runs the pilot image (docker) that runs Apptainer scanner clients # ######################################################################## +######################################################################## +# Common (platform-agnostic) setup +######################################################################## + # establish pilot's root path tmp_rootdir="$(pwd)/pilot-$(uuidgen)" -mkdir $tmp_rootdir -cd $tmp_rootdir +mkdir "$tmp_rootdir" +cd "$tmp_rootdir" export EWMS_PILOT_DATA_DIR_PARENT_PATH_ON_HOST="$tmp_rootdir" # mark startup.json's dir to be bind-mounted into the task container (by the pilot) @@ -21,27 +28,19 @@ export EWMS_PILOT_DATA_DIR_PARENT_PATH_ON_HOST="$tmp_rootdir" python -c 'import os; assert os.listdir(os.path.dirname(os.environ["CI_SKYSCAN_STARTUP_JSON"])) == ["startup.json"]' export EWMS_PILOT_EXTERNAL_DIRECTORIES="$(dirname "$CI_SKYSCAN_STARTUP_JSON")" -# task image, args, env -if [ -n "${_RUN_THIS_SIF_IMAGE:-}" ]; then - # place a duplicate of the file b/c the pilot transforms this into another format, so there are issues w/ parallelizing - export EWMS_PILOT_TASK_IMAGE="$tmp_rootdir/$(basename "$_RUN_THIS_SIF_IMAGE")" - cp "$_RUN_THIS_SIF_IMAGE" "$EWMS_PILOT_TASK_IMAGE" - export _EWMS_PILOT_CONTAINER_PLATFORM="apptainer" -else - export EWMS_PILOT_TASK_IMAGE="$CI_DOCKER_IMAGE_TAG" - export _EWMS_PILOT_CONTAINER_PLATFORM="docker" # NOTE: technically not needed b/c this is the default value - export _EWMS_PILOT_DOCKER_SHM_SIZE="6gb" # this only needed in ci--the infra would set this in prod -fi +# args export EWMS_PILOT_TASK_ARGS="python -m skymap_scanner.client --infile {{INFILE}} --outfile {{OUTFILE}} --client-startup-json $CI_SKYSCAN_STARTUP_JSON" -json_var=$(env | grep -E '^(SKYSCAN_|_SKYSCAN_)' | awk -F= '{printf "\"%s\":\"%s\",", $1, $2}' | sed 's/,$//') # must remove last comma + +# marshal SKYSCAN/_SKYSCAN_ env into JSON +json_var=$(env | grep -E '^(SKYSCAN_|_SKYSCAN_)' | awk -F= '{printf "\"%s\":\"%s\",", $1, $2}' | sed 's/,$//') json_var="{$json_var}" export EWMS_PILOT_TASK_ENV_JSON="$json_var" -# file types -- controls intermittent serialization +# file types export EWMS_PILOT_INFILE_EXT="JSON" export EWMS_PILOT_OUTFILE_EXT="JSON" -# Load JSON values +# queues/broker config export EWMS_PILOT_QUEUE_INCOMING=$(jq -r '.toclient.name' "$_EWMS_JSON_ON_HOST") export EWMS_PILOT_QUEUE_INCOMING_AUTH_TOKEN=$(jq -r '.toclient.auth_token' "$_EWMS_JSON_ON_HOST") export EWMS_PILOT_QUEUE_INCOMING_BROKER_TYPE=$(jq -r '.toclient.broker_type' "$_EWMS_JSON_ON_HOST") @@ -53,11 +52,62 @@ export EWMS_PILOT_QUEUE_OUTGOING_BROKER_TYPE=$(jq -r '.fromclient.broker_type' " export EWMS_PILOT_QUEUE_OUTGOING_BROKER_ADDRESS=$(jq -r '.fromclient.broker_address' "$_EWMS_JSON_ON_HOST") -# run! -ENV="$(dirname $tmp_rootdir)/pyenv-$(basename $tmp_rootdir)" -pip install virtualenv -virtualenv --python python3 "$ENV" -. "$ENV"/bin/activate -pip install --upgrade pip -pip install ewms-pilot[rabbitmq] -python -m ewms_pilot +######################################################################## +# Run! +######################################################################## + +# ─────────────── Case: Docker ─────────────── +if [[ "${_CI_SCANNER_CONTAINER_PLATFORM}" == "docker" ]]; then + + # docker-specific pilot env vars + export EWMS_PILOT_TASK_IMAGE="$_SCANNER_IMAGE_DOCKER" + export _EWMS_PILOT_DOCKER_SHM_SIZE="6gb" # CI-specific; prod infra should set this + + # Required env for the helper + export DOOD_OUTER_IMAGE="$_PILOT_IMAGE_FOR_DOCKER_SCANNER_CLIENT" + # Network for the outer container + if [[ -z "${DOOD_NETWORK:-}" ]]; then + echo "::error::DOOD_NETWORK must be set — this should've been set in '.github/workflows/tests.yml'. Did it not get forwarded to this script?" + exit 1 + fi + # Bind dirs: the pilot needs these paths visible at the same locations + # - tmp_rootdir (RW) + # - startup.json's parent (RO) + export DOOD_BIND_RW_DIRS="$tmp_rootdir" + export DOOD_BIND_RO_DIRS="$(dirname "$CI_SKYSCAN_STARTUP_JSON")" + # Forward envs by prefix and explicit list + export DOOD_FORWARD_ENV_PREFIXES="EWMS_ _EWMS_ SKYSCAN_ _SKYSCAN_" + export DOOD_FORWARD_ENV_VARS="CI_SKYSCAN_STARTUP_JSON" + + # run (curl script first) + tmp_for_dood_sh=$(mktemp -d) + curl -fsSL "$CI_SCRIPT_URL_DOOD_RUN" -o "$tmp_for_dood_sh/run-dood.sh" + chmod +x "$tmp_for_dood_sh/run-dood.sh" + "$tmp_for_dood_sh/run-dood.sh" + +# ─────────────── Case: Apptainer ─────────────── +elif [[ "${_CI_SCANNER_CONTAINER_PLATFORM}" == "apptainer" ]]; then + + # apptainer-specific pilot env vars + export EWMS_PILOT_TASK_IMAGE="$_SCANNER_IMAGE_APPTAINER" + export _EWMS_PILOT_APPTAINER_IMAGE_DIRECTORY_MUST_BE_PRESENT=True + + # run + docker run --rm \ + --privileged \ + --network=host \ + \ + -v "$tmp_rootdir:$tmp_rootdir" \ + -v "$(dirname "$CI_SKYSCAN_STARTUP_JSON"):$(dirname "$CI_SKYSCAN_STARTUP_JSON")":ro \ + -v "$_SCANNER_IMAGE_APPTAINER:$_SCANNER_IMAGE_APPTAINER":ro \ + \ + --env CI_SKYSCAN_STARTUP_JSON="$CI_SKYSCAN_STARTUP_JSON" \ + $(env | grep -E '^(EWMS_|_EWMS_)' | cut -d'=' -f1 | sed 's/^/--env /') \ + \ + "$_PILOT_IMAGE_FOR_APPTAINER_SCANNER_CLIENT" + +# ─────────────── Case: Unknown??? ─────────────── +else + echo "::error::cannot launch worker — unknown '_CI_SCANNER_CONTAINER_PLATFORM=$_CI_SCANNER_CONTAINER_PLATFORM'" + exit 2 +fi diff --git a/resources/launch_scripts/local_scan.py b/resources/launch_scripts/local_scan.py index e3218f8e2..57fe6ca8c 100644 --- a/resources/launch_scripts/local_scan.py +++ b/resources/launch_scripts/local_scan.py @@ -1,6 +1,7 @@ """Runs a scanner instance (server & workers) all on the same machine.""" import argparse +import json import os import subprocess import sys @@ -8,8 +9,10 @@ from datetime import datetime from pathlib import Path from collections import deque +from typing import TypeAlias TAIL = int(os.getenv("CI_LOCAL_SCAN_TAIL", 5)) +ProcessTuple: TypeAlias = tuple[str, subprocess.Popen, Path] def _print_now(string: str) -> None: @@ -23,6 +26,7 @@ def _print_now(string: str) -> None: def parse_args(): + """Parse CLI arguments.""" parser = argparse.ArgumentParser( description="Run a scanner instance (server and workers) on the same machine." ) @@ -39,13 +43,15 @@ def parse_args(): return parser.parse_args() -def _terminate_all(processes: list[tuple[str, subprocess.Popen, Path]]) -> None: +def _terminate_all(processes: list[ProcessTuple]) -> None: + """Terminate all processes and give them a moment to exit.""" for _, p, _ in processes: p.terminate() time.sleep(10) -def validate_env_vars(): +def validate_env_vars() -> None: + """Ensure required env vars exist and their directories are created.""" required = [ "CI_SKYSCAN_CACHE_DIR", "CI_SKYSCAN_OUTPUT_DIR", @@ -57,7 +63,8 @@ def validate_env_vars(): Path(os.environ[var]).mkdir(parents=True, exist_ok=True) -def wait_for_file(path: Path, timeout: int = 60): +def wait_for_file(path: Path, timeout: int = 60) -> None: + """Block until a file exists or time out with an error.""" for _ in range(timeout): if path.exists(): return @@ -68,7 +75,12 @@ def wait_for_file(path: Path, timeout: int = 60): sys.exit(1) -def launch_process(cmd, stdout_file, cwd=None) -> subprocess.Popen: +def launch_process( + cmd, + stdout_file: Path | None, + cwd: Path | None = None, +) -> subprocess.Popen: + """Launch a subprocess with optional stdout redirection and cwd.""" _print_now(f"Launching process: {cmd}") return subprocess.Popen( cmd, @@ -79,18 +91,20 @@ def launch_process(cmd, stdout_file, cwd=None) -> subprocess.Popen: def build_server_cmd(outdir: Path, startup_json: Path) -> list[str]: + """Build the command used to launch the server (Docker or Apptainer).""" threshold = os.getenv("_PREDICTIVE_SCANNING_THRESHOLD") - if threshold: - predictive = ["--predictive-scanning-threshold", threshold] - else: - predictive = [] - - if os.getenv("_RUN_THIS_SIF_IMAGE"): - os.environ["SKYSCAN_EWMS_JSON"] = os.environ["_EWMS_JSON_ON_HOST"] + predictive = ["--predictive-scanning-threshold", threshold] if threshold else [] + + if os.environ["_CI_SCANNER_CONTAINER_PLATFORM"] == "apptainer": + if not os.environ["_SCANNER_IMAGE_APPTAINER"]: + raise RuntimeError( + "env var '_SCANNER_IMAGE_APPTAINER' must be set when '_CI_SCANNER_CONTAINER_PLATFORM=apptainer'" + ) + os.environ["SKYSCAN_EWMS_JSON"] = os.environ["_EWMS_JSON_ON_HOST"] # forward return [ "singularity", "run", - os.environ["_RUN_THIS_SIF_IMAGE"], + os.environ["_SCANNER_IMAGE_APPTAINER"], # "python", "-m", @@ -109,15 +123,15 @@ def build_server_cmd(outdir: Path, startup_json: Path) -> list[str]: *os.environ["_NSIDES"].split(), "--simulated-event", ] - else: - env_flags = [] + elif os.environ["_CI_SCANNER_CONTAINER_PLATFORM"] == "docker": + env_flags: list[str] = [] for key in os.environ: if key.startswith(("SKYSCAN_", "_SKYSCAN_", "EWMS_", "_EWMS_")): env_flags.extend(["--env", key]) return [ "docker", "run", - "--network=host", + f"--network={os.environ['DOOD_NETWORK']}", "--rm", # "--mount", @@ -143,7 +157,7 @@ def build_server_cmd(outdir: Path, startup_json: Path) -> list[str]: "--env", f"SKYSCAN_EWMS_JSON=/local/ewms/{Path(os.environ['_EWMS_JSON_ON_HOST']).name}", # - os.environ["CI_DOCKER_IMAGE_TAG"], + os.environ["_SCANNER_IMAGE_DOCKER"], # "python", "-m", @@ -163,6 +177,10 @@ def build_server_cmd(outdir: Path, startup_json: Path) -> list[str]: *predictive, "--real-event", ] + else: + raise RuntimeError( + f"unknown '_CI_SCANNER_CONTAINER_PLATFORM': {os.environ['_CI_SCANNER_CONTAINER_PLATFORM']}" + ) def _last_n_lines(fpath: Path, n: int) -> list[str]: @@ -176,40 +194,42 @@ def _last_n_lines(fpath: Path, n: int) -> list[str]: return [f""] -def main(): - processes: list[tuple[str, subprocess.Popen, Path]] = [] - args = parse_args() - - # Validate directories - args.output_dir.mkdir(parents=True, exist_ok=True) - validate_env_vars() - +def _validate_launch_dir() -> Path: + """Ensure we are running from the expected directory and return it.""" launch_dir = Path.cwd() if launch_dir.name != "launch_scripts": sys.exit("Script must be run from 'resources/launch_scripts' directory") + return launch_dir - # Setup startup JSON + +def _setup_startup_json(launch_dir: Path) -> Path: + """Create startup.json path, ensure parent exists, and export its env var.""" startup_json = launch_dir / "dir-for-startup-json" / "startup.json" startup_json.parent.mkdir(parents=True, exist_ok=True) os.environ["CI_SKYSCAN_STARTUP_JSON"] = str(startup_json) + return startup_json + - # Launch server +def _start_server(outdir: Path, startup_json: Path) -> ProcessTuple: + """Start the central server process and return its tuple.""" _print_now("Launching server...") - server_cmd = build_server_cmd(args.output_dir, startup_json) - server_log = args.output_dir / "server.out" + server_cmd = build_server_cmd(outdir, startup_json) + server_log = outdir / "server.out" server_proc = launch_process(server_cmd, stdout_file=server_log) - processes.append(("central server", server_proc, server_log)) + return ("central server", server_proc, server_log) - # Wait for startup.json - _print_now("Waiting for startup.json...") - wait_for_file(startup_json) - # Launch workers - _print_now(f"Launching {args.n_workers} workers...") +def _start_workers( + n_workers: int, launch_dir: Path, outdir: Path +) -> list[ProcessTuple]: + """Start N worker processes and return their tuples.""" if "EWMS_PILOT_TASK_TIMEOUT" not in os.environ: os.environ["EWMS_PILOT_TASK_TIMEOUT"] = str(30 * 60) # 30 mins - for i in range(1, args.n_workers + 1): - worker_dir = args.output_dir / f"worker-{i}" + + processes: list[ProcessTuple] = [] + _print_now(f"Launching {n_workers} workers...") + for i in range(1, n_workers + 1): + worker_dir = outdir / f"worker-{i}" worker_dir.mkdir(parents=True, exist_ok=True) out_path = worker_dir / "pilot.out" proc = launch_process( @@ -219,31 +239,44 @@ def main(): ) processes.append((f"worker #{i}", proc, out_path)) _print_now(f"\tworker #{i} launched") + return processes + + +def _periodic_status(i: int, n_procs: int, og_n_procs: int) -> None: + """Print periodic status header lines.""" + if i % 6 == 1: + _print_now(f"{n_procs}/{og_n_procs} scan processes are running.") + if i % 6 == 0: + _print_now( + f"checking in on all {n_procs}/{og_n_procs} running scan processes..." + ) + _print_now("- - - - -") + - # Wait for all processes to finish +def _maybe_tail(name: str, log: Path, i: int) -> None: + """Tail the last N lines of a process log on schedule.""" + if i % 6 == 0: + _print_now(f"{name} 'tail -{TAIL} {log}':") + for ln in _last_n_lines(log, TAIL): + _print_now(f"\t>>>\t{ln}") + _print_now("- - - - -") + + +def _monitor_until_done(processes: list[ProcessTuple]) -> None: + """Monitor processes, tail logs periodically, and handle failures.""" + og_len = len(processes) i = -1 while processes: i += 1 - if i % 6 == 1: # every 1 min, print -- offset with the 'print' below - _print_now("scan processes are all running.") + _periodic_status(i, len(processes), og_len) time.sleep(10) - if i % 6 == 0: # every 1 min, print - _print_now("checking in on scan processes...") - _print_now("- - - - -") - # check all processes for name, proc, log in list(processes): ret = proc.poll() + _maybe_tail(name, log, i) - if i % 6 == 0: - _print_now(f"{name} 'tail -{TAIL} {log}':") - for ln in _last_n_lines(log, TAIL): - _print_now(f"\t>>>\t{ln}") - _print_now("- - - - -") - - # is it done? if ret is None: - continue + continue # still running # it's done _print_now(f"Process {name} exited with code {ret}") @@ -261,5 +294,38 @@ def main(): _print_now("All components finished successfully") +def main() -> None: + """Entry point for launching server and workers on the same machine.""" + processes: list[ProcessTuple] = [] + args = parse_args() + + if not os.getenv("_CI_SCANNER_CONTAINER_PLATFORM"): + raise RuntimeError("must provide env var '_CI_SCANNER_CONTAINER_PLATFORM'") + + args.output_dir.mkdir(parents=True, exist_ok=True) + validate_env_vars() + + launch_dir = _validate_launch_dir() + startup_json = _setup_startup_json(launch_dir) + + # start server + processes.append(_start_server(args.output_dir, startup_json)) + + # wait for 'startup.json' file + _print_now("Waiting for startup.json...") + wait_for_file(startup_json) + with open(startup_json) as f: + _str = json.dumps(json.load(f), indent=4) + _str = "\n".join( # we don't need to see all the lonnnng data, just some is ok + x if len(x) < (80 + 1) else f"{x[:(80-3)]}..." for x in _str.splitlines() + ) + _print_now(_str) + + # start worker(s) + processes.extend(_start_workers(args.n_workers, launch_dir, args.output_dir)) + + _monitor_until_done(processes) + + if __name__ == "__main__": main() diff --git a/skymap_scanner/server/__init__.py b/skymap_scanner/server/__init__.py index d7c2f5438..86d041549 100644 --- a/skymap_scanner/server/__init__.py +++ b/skymap_scanner/server/__init__.py @@ -50,6 +50,15 @@ class ServerEnvConfig: SKYSCAN_KILL_SWITCH_CHECK_INTERVAL: int = 5 * 60 + SKYSCAN_MQ_PREFETCH_FROM_CLIENTS: int = ( + # What is prefetch? + # Maximum number of unacknowledged messages the consumer is allowed to have in-flight. + # With prefetch=1, mqclient waits for each ack before delivering the next message. + # Increasing this (e.g., 300) lets mqclient stream messages continuously without waiting. + # Going much higher (>300) typically gives only small additional throughput gains (diminishing returns). + 300 + ) + # TIMEOUTS # # seconds -- how long server waits before thinking *all* clients are dead diff --git a/skymap_scanner/server/utils.py b/skymap_scanner/server/utils.py index d8a94c541..896c27636 100644 --- a/skymap_scanner/server/utils.py +++ b/skymap_scanner/server/utils.py @@ -43,6 +43,7 @@ def get_mqclient_connections() -> tuple[mq.Queue, mq.Queue]: name=ewms_config["fromclient"]["name"], auth_token=ewms_config["fromclient"]["auth_token"], timeout=SERVER_ENV.SKYSCAN_MQ_TIMEOUT_FROM_CLIENTS, + prefetch=SERVER_ENV.SKYSCAN_MQ_PREFETCH_FROM_CLIENTS, ) return to_clients_queue, from_clients_queue From 808e0ba502019cac5ed3371319e809ae0d406b0a Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 11 Mar 2026 20:57:04 +0000 Subject: [PATCH 07/63] update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ca3036dc1..29a494ec6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ dependencies = [ ] dynamic = ["version"] # do not edit — autogenerated by wipac-dev-py-setup-action name = "skymap-scanner" # do not edit — autogenerated by wipac-dev-py-setup-action -requires-python = ">=3.10, <3.15" # do not edit — autogenerated by wipac-dev-py-setup-action +requires-python = ">=3.10, <3.14" # do not edit — autogenerated by wipac-dev-py-setup-action keywords = [ 'WIPAC', 'IceCube', From f79d4c47c7fa758a51d1d0cc2566ae9f7ad1c21c Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Fri, 27 Mar 2026 17:19:48 -0400 Subject: [PATCH 08/63] fix formatting issues in monopod_taupede class and add an installation of the reco repository to the Docker file --- Dockerfile | 19 ++ skymap_scanner/recos/monopod_taupede.py | 238 ++++++++++++------------ 2 files changed, 134 insertions(+), 123 deletions(-) diff --git a/Dockerfile b/Dockerfile index 34bd38d81..a9b6019cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,6 +35,25 @@ RUN --mount=type=bind,source=.,target=/src,rw \ --mount=type=cache,target=/tmp/pip-cache \ pip install /src[rabbitmq] + + +#INSTALL RECO REPOSITORY +RUN mkdir /opt/reco_src +WORKDIR /opt/reco_src +RUN git init && git checkout -b && git config advice.detachedHead false +RUN --mount=type=secret,id=GHTOKEN GHTOKEN=$(cat /run/secrets/GHTOKEN) && curl -sS -f -I -H "Authorization: token ${GHTOKEN}" https://api.github.com && \ + git pull --depth 1 https://${GHTOKEN}@github.com/icecube/reco.git RUN --mount=type=cache,target=/tmp/pip-cache \ + pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" +RUN --mount=type=bind,source=.,target=/src,rw \ + --mount=type=cache,target=/tmp/pip-cache \ + pip install /opt/reco_src + + + + + + + # optional diagnostics RUN python --version RUN pip freeze diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 61b9af8a9..45a4c18f7 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,5 +1,4 @@ - - +from common.calculator import poisson_llh import datetime import random import time @@ -20,9 +19,10 @@ from icecube.icetray import I3Frame # type: ignore[import] -from .. import config as cfg -from ..utils.pixel_classes import RecoPixelVariation -from . import RecoInterface, VertexGenerator +from skymap_scanner import config as cfg +from skymap_scanner.utils.pixel_classes import RecoPixelVariation +from skymap_scanner.recos import RecoInterface, VertexGenerator +from skymap_scanner.recos.common.pulse_proc import mask_deepcore, pulse_cleaning #IMPORTS FROM REC_TAU import os @@ -54,6 +54,7 @@ from icecube.gulliver_modules import gulliview from snowflake import library, unfold +import reco from reco import skymap, dom from reco.masks import (earlypulses, maskdc, @@ -116,8 +117,7 @@ def setup_reco(self): tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), quantileEpsilon=1, effectivedistancetableprob = effp_spline, - effectivedistancetabletmod = tmod_spline - ) + effectivedistancetabletmod = tmod_spline) self.muon_service = None @@ -130,7 +130,7 @@ def setup_reco(self): @icetray.traysegment def prepare_frames(tray, name, logger, **kwargs) -> None: #CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED - # Generates the vertex seed for the initial scan. + #Generates the vertex seed for the initial scan. # Only run if HESE_VHESelfVeto is not present in the frame. # VertexThreshold is 250 in the original HESE analysis (Tianlu) # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. @@ -139,8 +139,7 @@ def extract_seed(frame): frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] - tray.Add(extract_seed, "ExtractSeed", - If = lambda frame: frame.Has("HESE_VHESelfVeto")) + tray.Add(extract_seed, "ExtractSeed", If = lambda frame: frame.Has("HESE_VHESelfVeto")) tray.AddModule('VHESelfVeto', 'selfveto', VertexThreshold=250, @@ -152,16 +151,16 @@ def extract_seed(frame): # this only runs if the previous module did not return anything tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', - VertexThreshold=5, - Pulses=self.pulsesName_input+'HLC', - OutputBool='VHESelfVeto_meaningless_lowen', - OutputVertexTime=cfg.INPUT_TIME_NAME, - OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: not frame.Has("HESE_VHESelfVeto")) + VertexThreshold=5, + Pulses=self.pulsesName_input+'HLC', + OutputBool='VHESelfVeto_meaningless_lowen', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: not frame.Has("HESE_VHESelfVeto")) tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) - #OTHER METHODS FROM MILLIPEDE_WILKS: + #OTHER METHODS FROM MILLIPEDE_WILKS: def makeSurePulsesExist(frame, pulsesName) -> None: if pulsesName not in frame: raise RuntimeError(f"{pulsesName} not in frame") @@ -173,11 +172,11 @@ def makeSurePulsesExist(frame, pulsesName) -> None: @icetray.traysegment def exclusions(self, tray, name): tray.Add('Delete', keys=['BrightDOMs', - 'SaturatedDOMs', - 'DeepCoreDOMs', - self.pulsesName_cleaned, - self.pulsesName_cleaned+'TimeWindows', - self.pulsesName_cleaned+'TimeRange']) + 'SaturatedDOMs', + 'DeepCoreDOMs', + self.pulsesName_cleaned, + self.pulsesName_cleaned+'TimeWindows', + self.pulsesName_cleaned+'TimeRange']) exclusionList = \ tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', @@ -188,12 +187,11 @@ def exclusions(self, tray, name): BrightDOMThreshold=2, BadDomsList='BadDomsList', CalibrationErrata='CalibrationErrata', - SaturationWindows='SaturationWindows' - ) + SaturationWindows='SaturationWindows') - # I like having frame objects in there even if they are empty for some frames + #I like having frame objects in there even if they are empty for some frames def createEmptyDOMLists(frame, ListNames=[]): for name in ListNames: if name in frame: @@ -207,8 +205,7 @@ def createEmptyDOMLists(frame, ListNames=[]): def skipunhits(frame, output, pulses): keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] keepoms = list(range(1,60,5)) - all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame( - frame, pulses) + all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame(frame, pulses) omgeo = frame['I3Geometry'] geo = omgeo.omgeo unhits = dataclasses.I3VectorOMKey() @@ -224,11 +221,11 @@ def skipunhits(frame, output, pulses): frame[output] = unhits - ################## + ################## tray.AddModule(pulse_cleaning, "LatePulseCleaning", - input_pulses_name=self.pulsesName, - output_pulses_name=self.pulsesName_cleaned, - residual=1.5e3*I3Units.ns) + input_pulses_name=self.pulsesName, + output_pulses_name=self.pulsesName_cleaned, + residual=1.5e3*I3Units.ns) ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) @@ -246,9 +243,9 @@ def sane(frame, split_names): return False - ''' def print_frameid(frame): - eventid = frame['I3EventHeader'].event_id - print("*******Currently processing frame %s*******" %eventid)''' + '''def print_frameid(frame): + eventid = frame['I3EventHeader'].event_id + print("*******Currently processing frame %s*******" %eventid)''' def fixed_dir(filelist, isdata, hypo, split_names, nframes=None): @@ -275,7 +272,6 @@ def extract(frame): return truths[0] - ) @staticmethod @@ -332,23 +328,23 @@ def notify0(frame): #SETTING PULSES FOR RECO TO DEFAULT pulses_for_reco='SplitInIcePulses' millipede_params = {'Pulses': f'{pulses_for_reco}PulseCleaned', - 'CascadePhotonicsService': self.cascade_service, - 'MuonPhotonicsService': None, - 'ExcludedDOMs': self.excludedDOMs, - 'ReadoutWindow': f'{pulses_for_reco}PulseCleanedTimeRange', - 'PartialExclusion': True, - 'PhotonsPerBin': 0, - 'UseUnhitDOMs': not False, - 'MinTimeWidth': 16, - 'BinSigma': np.nan, - 'RelUncertainty': 0.05,'StepZenith':0,'StepAzimuth':0} + 'CascadePhotonicsService': self.cascade_service, + 'MuonPhotonicsService': None, + 'ExcludedDOMs': self.excludedDOMs, + 'ReadoutWindow': f'{pulses_for_reco}PulseCleanedTimeRange', + 'PartialExclusion': True, + 'PhotonsPerBin': 0, + 'UseUnhitDOMs': not False, + 'MinTimeWidth': 16, + 'BinSigma': np.nan, + 'RelUncertainty': 0.05,'StepZenith':0,'StepAzimuth':0} icetray.logging.log_info(pformat(millipede_params), __name__) minis = [_ for _ in ['MIGRAD', - 'iMIGRAD', - 'SIMPLEX', - 'iSIMPLEX', - 'LBFGSB'] + 'iMIGRAD', + 'SIMPLEX', + 'iSIMPLEX', + 'LBFGSB']] sfx='PPB0' for mini in minis: @@ -360,96 +356,93 @@ def notify0(frame): Chain=1, Iterations=iterations, **millipede_params) - seeder = lilliput.segments.add_seed_service( - tray, - millipede_params['Pulses'], - [f'{specifier}_{mini}_{PPB0}']) - minispec = mini.lower() - relerr=0.05 - minispec += f'.relerr{relerr:.2f}' - - prefs = [_ for tup in [[f'TaupedeFit_{mini}_{PPB0}', - f'MonopodFit_{mini}_{PPB0}'] - for mini in minis] - for _ in tup] - tray.Add(preferred, + seeder = lilliput.segments.add_seed_service( + tray, + millipede_params['Pulses'], + [f'{specifier}_{mini}_{PPB0}']) + minispec = mini.lower() + relerr=0.05 + minispec += f'.relerr{relerr:.2f}' + + prefs = [_ for tup in [[f'TaupedeFit_{mini}_{PPB0}', f'MonopodFit_{mini}_{PPB0}'] for mini in minis] for _ in tup] + tray.Add(preferred, i3_particles_fitparams=[(_, f'{_}FitParams') for _ in prefs], If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs])) - #print( prefs ) + #print( prefs ) - #print("running HESE, with printing modules") - from segments.MillipedeWrapper import MillipedeWrapper + #print("running HESE, with printing modules") + from segments.MillipedeWrapper import MillipedeWrapper - # energy definition - gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" - gcdfile = dataio.I3File(gcdfilepath) - frame = gcdfile.pop_frame() - - while 'I3Geometry' not in frame: + # energy definition + gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" + gcdfile = dataio.I3File(gcdfilepath) frame = gcdfile.pop_frame() - geometry = frame['I3Geometry'].omgeo - - strings = [1, 2, 3, 4, 5, 6, 13, 21, 30, 40, 50, 59, 67, 74, 73, 72, 78, 77, 76, 75, 68, 60, 51, 41, 31, 22, 14, 7] - - outerbounds = {} - cx, cy = [], [] - for string in strings: - omkey = icetray.OMKey(string, 1) - # if geometry.has_key(omkey): - x, y = geometry[omkey].position.x, geometry[omkey].position.y - outerbounds[string] = (x, y) - cx.append(x) - cy.append(y) - cx, cy = np.asarray(cx), np.asarray(cy) - order = np.argsort(np.arctan2(cx, cy)) - outeredge_x = cx[order] - outeredge_y = cy[order] - - #print(sfx) - - -#SHOULD I TAKE THIS OUT SINCE ITS TRACK -# track reco - tray.Add('I3OMSelection', 'omselection_HESE', - InputResponse = 'SRT' + "SplitInIcePulses", - OmittedStrings = [79,80,81,82,83,84,85,86], # deepcore strings - OutputOMSelection = f'SRTSplitInIcePulses_BadOMSelectionString_{sfx}', - OutputResponse = f"SRTSplitInIcePulses_IC_Singles_{sfx}") - - tray.Add(SPEFit, f'SPEFit16_{sfx}', + + while 'I3Geometry' not in frame: + frame = gcdfile.pop_frame() + geometry = frame['I3Geometry'].omgeo + + strings = [1, 2, 3, 4, 5, 6, 13, 21, 30, 40, 50, 59, 67, 74, 73, 72, 78, 77, 76, 75, 68, 60, 51, 41, 31, 22, 14, 7] + + outerbounds = {} + cx, cy = [], [] + for string in strings: + omkey = icetray.OMKey(string, 1) + #if geometry.has_key(omkey): + x, y = geometry[omkey].position.x, geometry[omkey].position.y + outerbounds[string] = (x, y) + cx.append(x) + cy.append(y) + cx, cy = np.asarray(cx), np.asarray(cy) + order = np.argsort(np.arctan2(cx, cy)) + outeredge_x = cx[order] + outeredge_y = cy[order] + + #print(sfx) + + + #SHOULD I TAKE THIS OUT SINCE ITS TRACK + #track reco + tray.Add('I3OMSelection', 'omselection_HESE', + InputResponse = 'SRT' + "SplitInIcePulses", + OmittedStrings = [79,80,81,82,83,84,85,86], # deepcore strings + OutputOMSelection = f'SRTSplitInIcePulses_BadOMSelectionString_{sfx}', + OutputResponse = f"SRTSplitInIcePulses_IC_Singles_{sfx}") + + tray.Add(SPEFit, f'SPEFit16_{sfx}', Pulses = f"SRTSplitInIcePulses_IC_Singles_{sfx}", Iterations = 16) - del millipede_params["PhotonsPerBin"] # also input to MillipedeWrapper next, gives error if entered twice + del millipede_params["PhotonsPerBin"] # also input to MillipedeWrapper next, gives error if entered twice - # HESE millipede - tray.Add(MillipedeWrapper, f'HESEMillipedeFit_{sfx}', - seed_cascade = f'MonopodFit_iMIGRAD_{sfx}', - seed_tau = f'TaupedeFit_iMIGRAD_{sfx}', - seed_track = f'SPEFit16_{sfx}', - PhotonsPerBin = 0, - ShowerSpacing = 5, - innerboundary=550, - outerboundary=650, - outeredge_x=outeredge_x, - outeredge_y=outeredge_y, - **millipede_params) - - - # rename - tray.Add('Rename', + #HESE millipede + tray.Add(MillipedeWrapper, f'HESEMillipedeFit_{sfx}', + seed_cascade = f'MonopodFit_iMIGRAD_{sfx}', + seed_tau = f'TaupedeFit_iMIGRAD_{sfx}', + seed_track = f'SPEFit16_{sfx}', + PhotonsPerBin = 0, + ShowerSpacing = 5, + innerboundary=550, + outerboundary=650, + outeredge_x=outeredge_x, + outeredge_y=outeredge_y, + **millipede_params) + + + #rename + tray.Add('Rename', Keys=['SRTSplitInIcePulses_IC_Singles', f'SRTSplitInIcePulses_IC_Singles_{sfx}', 'PreferredFit_key', f'PreferredFit_key_{sfx}', 'PreferredFit', f"PreferredFit_{sfx}"]) - #LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW + #LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW - def notify1(frame): + def notify1(frame): logger.debug(f"reco complete! {datetime.datetime.now()}") - tray.AddModule(notify1, "notify1") + tray.AddModule(notify1, "notify1") @staticmethod def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariation: @@ -462,8 +455,7 @@ def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariati posvar_id=frame[cfg.I3FRAME_POSVAR].value, position=frame["Dummy_pos"], time=frame["Dummy_time"].value, - energy=frame["Dummy_time"].value, - ) + energy=frame["Dummy_time"].value) # Provide a standard alias for the reconstruction class provided by this module. From 0e5feb841b26d47ecde651de503b6bb63ac88694 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Sun, 29 Mar 2026 17:38:31 -0400 Subject: [PATCH 09/63] "added branch name to the new branch created when installing reco library" --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a9b6019cf..f730cdbb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,7 +40,7 @@ RUN --mount=type=bind,source=.,target=/src,rw \ #INSTALL RECO REPOSITORY RUN mkdir /opt/reco_src WORKDIR /opt/reco_src -RUN git init && git checkout -b && git config advice.detachedHead false +RUN git init && git checkout -b {WORKDIR} && git config advice.detachedHead false RUN --mount=type=secret,id=GHTOKEN GHTOKEN=$(cat /run/secrets/GHTOKEN) && curl -sS -f -I -H "Authorization: token ${GHTOKEN}" https://api.github.com && \ git pull --depth 1 https://${GHTOKEN}@github.com/icecube/reco.git RUN --mount=type=cache,target=/tmp/pip-cache \ pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" From 72c4fb0b139ffd69cdfdd7fb7a940b7a8179c93f Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Sun, 29 Mar 2026 18:06:48 -0400 Subject: [PATCH 10/63] "edited imports to be relative" --- skymap_scanner/recos/monopod_taupede.py | 44 +++++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 45a4c18f7..bf5ba2891 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,31 +1,47 @@ -from common.calculator import poisson_llh +#MILLIPEDE WILKS IMPORTS: import datetime -import random -import time -from typing import List, Final +import os +from typing import Final, List, Tuple -from icecube.icetray import I3Units # type: ignore[import] -from icecube import ( # type: ignore[import] # noqa: F401 +import numpy +from icecube.icetray import I3Units +from icecube import ( # noqa: F401 + VHESelfVeto, dataclasses, frame_object_diff, gulliver, gulliver_modules, icetray, + lilliput, millipede, photonics_service, recclasses, - simclasses, + simclasses ) -from icecube.icetray import I3Frame # type: ignore[import] -from skymap_scanner import config as cfg -from skymap_scanner.utils.pixel_classes import RecoPixelVariation -from skymap_scanner.recos import RecoInterface, VertexGenerator -from skymap_scanner.recos.common.pulse_proc import mask_deepcore, pulse_cleaning +from icecube.icetray import I3Frame -#IMPORTS FROM REC_TAU -import os +from .. import config as cfg +from ..utils.pixel_classes import RecoPixelVariation +from . import RecoInterface, VertexGenerator +from .common.pulse_proc import mask_deepcore, pulse_cleaning + + + + + + + + + + +from common.calculator import poisson_llh +import random +import time + + +#IMPORTS FROM REC_TAU: import argparse from importlib.metadata import version From 71d52babe9bc92538a4873550044b4ce9228ac57 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Sun, 29 Mar 2026 18:22:11 -0400 Subject: [PATCH 11/63] "deleted redudant and unused imports" --- skymap_scanner/recos/monopod_taupede.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index bf5ba2891..5d3edc230 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,10 +1,8 @@ -#MILLIPEDE WILKS IMPORTS: import datetime import os -from typing import Final, List, Tuple +from typing import Final, List, import numpy -from icecube.icetray import I3Units from icecube import ( # noqa: F401 VHESelfVeto, dataclasses, @@ -20,7 +18,7 @@ ) -from icecube.icetray import I3Frame +from icecube.icetray import I3Frame, I3Units, I3Tray from .. import config as cfg from ..utils.pixel_classes import RecoPixelVariation @@ -29,16 +27,7 @@ - - - - - - - -from common.calculator import poisson_llh import random -import time #IMPORTS FROM REC_TAU: @@ -49,11 +38,6 @@ import numpy as np from icecube import dataio -from icecube import (icetray, - dataclasses, - photonics_service, - mue) # noqa: F401 -from icecube.icetray import I3Tray, I3Units from icecube.phys_services.which_split import which_split from icecube.millipede import HighEnergyExclusions from icecube.spline_reco import SplineMPE @@ -66,7 +50,6 @@ from icecube.STTools.seededRT.configuration_services import I3DOMLinkSeededRTConfigurationService # for gulliver -from icecube import lilliput from icecube.gulliver_modules import gulliview from snowflake import library, unfold From 801f8fe6112519b083d95321b7d01ca59137f805 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Sun, 29 Mar 2026 18:26:31 -0400 Subject: [PATCH 12/63] 'fixed dangling comma' --- skymap_scanner/recos/monopod_taupede.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 5d3edc230..3de5d9987 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,6 +1,6 @@ import datetime import os -from typing import Final, List, +from typing import Final, List import numpy from icecube import ( # noqa: F401 From 0a8b8e383318974589287067d2c489b53dd52752 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Mon, 30 Mar 2026 10:17:28 -0400 Subject: [PATCH 13/63] "fixing spacing issue in dockerfile" --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f730cdbb2..377783719 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,8 @@ RUN mkdir /opt/reco_src WORKDIR /opt/reco_src RUN git init && git checkout -b {WORKDIR} && git config advice.detachedHead false RUN --mount=type=secret,id=GHTOKEN GHTOKEN=$(cat /run/secrets/GHTOKEN) && curl -sS -f -I -H "Authorization: token ${GHTOKEN}" https://api.github.com && \ - git pull --depth 1 https://${GHTOKEN}@github.com/icecube/reco.git RUN --mount=type=cache,target=/tmp/pip-cache \ + git pull --depth 1 https://${GHTOKEN}@github.com/icecube/reco.git +RUN --mount=type=cache,target=/tmp/pip-cache \ pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" RUN --mount=type=bind,source=.,target=/src,rw \ --mount=type=cache,target=/tmp/pip-cache \ From 344fc9c2af384cc2b79399da3cf1aeb8f992a55d Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Mon, 30 Mar 2026 10:35:33 -0400 Subject: [PATCH 14/63] "fixed syntax issue with installing reco library in dockerfile" --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 377783719..af03d1650 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,9 +40,9 @@ RUN --mount=type=bind,source=.,target=/src,rw \ #INSTALL RECO REPOSITORY RUN mkdir /opt/reco_src WORKDIR /opt/reco_src -RUN git init && git checkout -b {WORKDIR} && git config advice.detachedHead false +RUN git init && git checkout main RUN --mount=type=secret,id=GHTOKEN GHTOKEN=$(cat /run/secrets/GHTOKEN) && curl -sS -f -I -H "Authorization: token ${GHTOKEN}" https://api.github.com && \ - git pull --depth 1 https://${GHTOKEN}@github.com/icecube/reco.git + git pull https://${GHTOKEN}@github.com/icecube/reco.git RUN --mount=type=cache,target=/tmp/pip-cache \ pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" RUN --mount=type=bind,source=.,target=/src,rw \ From f4b520e1a60caa25104406fb824095bdcac642f9 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Mon, 30 Mar 2026 16:14:34 -0400 Subject: [PATCH 15/63] reconfigure reco installation in docker file --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index af03d1650..5e829b4a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,9 +40,9 @@ RUN --mount=type=bind,source=.,target=/src,rw \ #INSTALL RECO REPOSITORY RUN mkdir /opt/reco_src WORKDIR /opt/reco_src -RUN git init && git checkout main +#RUN git init && git checkout main RUN --mount=type=secret,id=GHTOKEN GHTOKEN=$(cat /run/secrets/GHTOKEN) && curl -sS -f -I -H "Authorization: token ${GHTOKEN}" https://api.github.com && \ - git pull https://${GHTOKEN}@github.com/icecube/reco.git + git clone https://${GHTOKEN}@github.com/icecube/reco.git . RUN --mount=type=cache,target=/tmp/pip-cache \ pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" RUN --mount=type=bind,source=.,target=/src,rw \ From 429f461c56f7e18149565d11f55d9d5b1c8237f2 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Mon, 30 Mar 2026 16:27:11 -0400 Subject: [PATCH 16/63] fix reco installation in docker file --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5e829b4a6..a2dda4c9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,8 +41,8 @@ RUN --mount=type=bind,source=.,target=/src,rw \ RUN mkdir /opt/reco_src WORKDIR /opt/reco_src #RUN git init && git checkout main -RUN --mount=type=secret,id=GHTOKEN GHTOKEN=$(cat /run/secrets/GHTOKEN) && curl -sS -f -I -H "Authorization: token ${GHTOKEN}" https://api.github.com && \ - git clone https://${GHTOKEN}@github.com/icecube/reco.git . +RUN --mount=type=secret,id=cvmfs_github_token cvmfs_github_token=$(cat /run/secrets/cvmfs_github_token) && curl -sS -f -I -H "Authorization: token ${cvmfs_github_token}" https://api.github.com && \ + git clone https://${cvmfs_github_token}@github.com/icecube/reco.git . RUN --mount=type=cache,target=/tmp/pip-cache \ pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" RUN --mount=type=bind,source=.,target=/src,rw \ From 55278b282d1d22bc70542548d13ebfa15de324e4 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 1 Apr 2026 16:34:19 -0400 Subject: [PATCH 17/63] implement ric's docker build fixes to publish.yml, tests.yml, and dockerfile --- .github/workflows/publish.yml | 2 +- .github/workflows/tests.yml | 5 ++++- Dockerfile | 13 +++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5a82bcaef..605435155 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: image-publish: - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.20 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.24 permissions: # for GITHUB_TOKEN packages: write with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 71326eeb6..5b1ee35e3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -240,6 +240,9 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: false # for checking if this builds, no loading needed + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + test-run-dummy: needs: [ flake8 ] runs-on: ubuntu-latest @@ -739,7 +742,7 @@ jobs: test-run-realistic, test-run-single-pixel, ] - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/tag-and-release.yml@v1.20 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/tag-and-release.yml@v1.24 permissions: # for GITHUB_TOKEN contents: write with: diff --git a/Dockerfile b/Dockerfile index a2dda4c9c..eee19e1a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,10 +38,15 @@ RUN --mount=type=bind,source=.,target=/src,rw \ #INSTALL RECO REPOSITORY -RUN mkdir /opt/reco_src -WORKDIR /opt/reco_src -#RUN git init && git checkout main -RUN --mount=type=secret,id=cvmfs_github_token cvmfs_github_token=$(cat /run/secrets/cvmfs_github_token) && curl -sS -f -I -H "Authorization: token ${cvmfs_github_token}" https://api.github.com && \ +RUN --mount=type=secret,id=github_token \ + git clone \ + https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ + /opt/reco_src +RUN --mount=type=cache,target=/tmp/pip-cache \ + pip install /opt/reco_src + + + cvmfs_github_token=$(cat /run/secrets/cvmfs_github_token) && curl -sS -f -I -H "Authorization: token ${cvmfs_github_token}" https://api.github.com && \ git clone https://${cvmfs_github_token}@github.com/icecube/reco.git . RUN --mount=type=cache,target=/tmp/pip-cache \ pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" From 536e819d8e4143ba7ec7a44b6702597380ec294c Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 08:53:31 -0400 Subject: [PATCH 18/63] deleted unecessary lines from docker file --- Dockerfile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index eee19e1a5..e6a4b8996 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,13 +46,6 @@ RUN --mount=type=cache,target=/tmp/pip-cache \ pip install /opt/reco_src - cvmfs_github_token=$(cat /run/secrets/cvmfs_github_token) && curl -sS -f -I -H "Authorization: token ${cvmfs_github_token}" https://api.github.com && \ - git clone https://${cvmfs_github_token}@github.com/icecube/reco.git . -RUN --mount=type=cache,target=/tmp/pip-cache \ - pip install --upgrade "pip>=25" "setuptools>=80" "wheel>=0.45" -RUN --mount=type=bind,source=.,target=/src,rw \ - --mount=type=cache,target=/tmp/pip-cache \ - pip install /opt/reco_src From 55071fe0ef8399891379e9a5395f58eceb46d37c Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 11:40:00 -0400 Subject: [PATCH 19/63] add access key to docker pushes --- .github/workflows/tests.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5b1ee35e3..3e052c0c1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -210,6 +210,8 @@ jobs: uses: docker/setup-buildx-action@v3 - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: docker/build-push-action@v6 + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile @@ -234,6 +236,8 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile @@ -274,6 +278,8 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile @@ -508,6 +514,8 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile @@ -607,6 +615,8 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile @@ -660,6 +670,8 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile From 28632d6365f33e219053eda0ddee5f61f47260c6 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 11:53:43 -0400 Subject: [PATCH 20/63] add access key to forgotten docker pushes --- .github/workflows/tests.yml | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3e052c0c1..414ed7a17 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -210,14 +210,14 @@ jobs: uses: docker/setup-buildx-action@v3 - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: docker/build-push-action@v6 - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -236,8 +236,6 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile @@ -278,15 +276,14 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" run: | @@ -514,14 +511,13 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} - name: run timeout-minutes: 55 # on average max~=35min run: | @@ -615,14 +611,13 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} - name: run run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" @@ -670,13 +665,13 @@ jobs: fetch-depth: 0 # setuptools-scm needs to access git tags - uses: docker/setup-buildx-action@v3 - uses: docker/build-push-action@v6 - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} with: context: . file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} - name: run timeout-minutes: 10 # on average max~=5min From 4e82ec56c86ac88a47e2f8404124c25ad2c44af7 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 12:01:46 -0400 Subject: [PATCH 21/63] fixed syntax errors in test.yaml additions --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 414ed7a17..fe1741c25 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -215,9 +215,9 @@ jobs: file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true - build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -281,9 +281,9 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" run: | From 92c3b0c94c87fd676dc12c3989d5b84fe59361de Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 12:09:23 -0400 Subject: [PATCH 22/63] added a return --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fe1741c25..d4b2c9e0b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -215,6 +215,7 @@ jobs: file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true + secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files From 0c81c1a6e1b8015fd5c94d0608827a2bf2ad373f Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 12:14:10 -0400 Subject: [PATCH 23/63] attempting different formatting in tests.yml --- .github/workflows/tests.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d4b2c9e0b..b158b20b5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -216,8 +216,7 @@ jobs: tags: skymap_scanner:py-dep-this load: true - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 From a461033b4147c764d1dcd0f0edbe435ca8cb06f9 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 12:22:12 -0400 Subject: [PATCH 24/63] reworking again --- .github/workflows/tests.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b158b20b5..8404d97ea 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -242,8 +242,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: false # for checking if this builds, no loading needed - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} test-run-dummy: needs: [ flake8 ] @@ -281,8 +280,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" @@ -516,8 +514,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} - name: run timeout-minutes: 55 # on average max~=35min run: | @@ -616,8 +613,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} - name: run run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" @@ -670,8 +666,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN}} + github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} - name: run timeout-minutes: 10 # on average max~=5min From fee9d1cb376ac126d2493593c66ac29084258a59 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:05:46 -0400 Subject: [PATCH 25/63] adding indents --- .github/workflows/tests.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8404d97ea..67b857d2f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -215,8 +215,8 @@ jobs: file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true - - github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} + secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -241,8 +241,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: false # for checking if this builds, no loading needed - - github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} + secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} test-run-dummy: needs: [ flake8 ] @@ -280,7 +280,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} + secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" @@ -514,7 +515,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} + secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 55 # on average max~=35min run: | @@ -613,7 +615,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} + secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" @@ -666,8 +669,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN}} - + secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 10 # on average max~=5min run: | From c28943a4cd5e59429f792331dd7751807553e545 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:09:34 -0400 Subject: [PATCH 26/63] adjusted order of build args and secrets --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 67b857d2f..243157d16 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -215,9 +215,9 @@ jobs: file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true + build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files secrets: |            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -280,9 +280,9 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files secrets: |            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" run: | From 2f37c545b49dc68659d9d6fc1835565a395faa55 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:12:43 -0400 Subject: [PATCH 27/63] added returns again --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 243157d16..32f69a911 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -218,6 +218,7 @@ jobs: build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files secrets: |            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -283,6 +284,7 @@ jobs: build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files secrets: |            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" run: | From 6bbbb639de1b8dac3c88a170572c7dca50ce0561 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:19:12 -0400 Subject: [PATCH 28/63] got rid of tabs --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 32f69a911..0055f2be5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -283,7 +283,7 @@ jobs: load: true build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" From 7a08ded887ab1b0ad224b0a570c9c7f5403ba2a1 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:21:30 -0400 Subject: [PATCH 29/63] commented out a line --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0055f2be5..b8d0d5550 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -216,8 +216,8 @@ jobs: tags: skymap_scanner:py-dep-this load: true build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + ' secrets: | +            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }}' - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 From df4c56b31197eef98bed127db92658649f0716b7 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:26:47 -0400 Subject: [PATCH 30/63] reverting to old version --- .github/workflows/tests.yml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b8d0d5550..7d159aa46 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -216,9 +216,6 @@ jobs: tags: skymap_scanner:py-dep-this load: true build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - ' secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }}' - - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -243,7 +240,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: false # for checking if this builds, no loading needed secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} test-run-dummy: needs: [ flake8 ] @@ -282,8 +279,6 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - if: ${{ matrix.container_platform == 'apptainer' }} name: "apptainer only: install apptainer" @@ -517,8 +512,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + - name: run timeout-minutes: 55 # on average max~=35min run: | @@ -617,8 +611,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + - name: run run: | set -euo pipefail; echo "now: $(date -u +"%Y-%m-%dT%H:%M:%S.%3N")" @@ -671,8 +664,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | -            github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + - name: run timeout-minutes: 10 # on average max~=5min run: | From c8ed73599ea8d08f966f01d02786609dff0e3649 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:47:45 -0400 Subject: [PATCH 31/63] hopefully added things with correct syntax --- .github/workflows/tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7d159aa46..d70cf1345 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -215,6 +215,7 @@ jobs: file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true + secrets: | github_token=$${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -278,6 +279,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} @@ -512,6 +514,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 55 # on average max~=35min @@ -611,6 +614,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run run: | @@ -664,6 +668,7 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true + secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 10 # on average max~=5min From 190d1d7aa707248ab716ce616b3541988085199f Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 13:49:28 -0400 Subject: [PATCH 32/63] test --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d70cf1345..2d1aa82dd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -668,7 +668,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 10 # on average max~=5min From 876ca802a601a7e2e429eb8ff6384bff27cef9a5 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 14:01:30 -0400 Subject: [PATCH 33/63] fixed? --- .github/workflows/tests.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2d1aa82dd..90fe5307f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -215,7 +215,8 @@ jobs: file: ./Dockerfile tags: skymap_scanner:py-dep-this load: true - secrets: | github_token=$${{ secrets.PERSONAL_ACCESS_TOKEN }} + secrets: | + github_token=$${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -279,7 +280,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} @@ -514,7 +516,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 55 # on average max~=35min @@ -614,7 +617,8 @@ jobs: file: ./Dockerfile tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true - secrets: | github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run run: | From 766c9379d76b1b9637ad9f255f4773957fa9f21a Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 16:22:29 -0400 Subject: [PATCH 34/63] remove tabs in docker file --- Dockerfile | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index e6a4b8996..2baf28b25 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,23 +35,13 @@ RUN --mount=type=bind,source=.,target=/src,rw \ --mount=type=cache,target=/tmp/pip-cache \ pip install /src[rabbitmq] - - #INSTALL RECO REPOSITORY RUN --mount=type=secret,id=github_token \ - git clone \ - https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ - /opt/reco_src + git clone \ + https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ + /opt/reco_src RUN --mount=type=cache,target=/tmp/pip-cache \ - pip install /opt/reco_src - - - - - - - - + pip install /opt/reco_src # optional diagnostics RUN python --version From 49d32abfecbf03a99217830d10bbf8d1c3f93a26 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 2 Apr 2026 16:31:59 -0400 Subject: [PATCH 35/63] remove tabs for real this time --- Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2baf28b25..62a6288f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,11 +37,12 @@ RUN --mount=type=bind,source=.,target=/src,rw \ #INSTALL RECO REPOSITORY RUN --mount=type=secret,id=github_token \ - git clone \ - https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ - /opt/reco_src + git clone \ + https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ + /opt/reco_src RUN --mount=type=cache,target=/tmp/pip-cache \ - pip install /opt/reco_src + pip install /opt/reco_src + # optional diagnostics RUN python --version From 233a2a443e9308cd8dc0651674ed093f0a4116ec Mon Sep 17 00:00:00 2001 From: ric-evans Date: Thu, 2 Apr 2026 16:36:58 -0500 Subject: [PATCH 36/63] pass dockerfile secret to `image-publish.yml` --- .github/workflows/publish.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 605435155..3e8c8dc0f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: image-publish: - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.24 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@docker-build-secret permissions: # for GITHUB_TOKEN packages: write with: @@ -32,3 +32,5 @@ jobs: registry_username: ${{ secrets.DOCKERHUB_USERNAME }} registry_token: ${{ secrets.DOCKERHUB_TOKEN }} cvmfs_github_token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} + dockerfile_secret_name_0: 'github_token' + dockerfile_secret_value_0: ${{ secrets.PERSONAL_ACCESS_TOKEN }} From 039a9d15e8af44397650f935a6a5ddae4044849b Mon Sep 17 00:00:00 2001 From: ric-evans Date: Thu, 2 Apr 2026 17:15:14 -0500 Subject: [PATCH 37/63] use `WIPACrepo/wipac-dev-workflows/...@v1.25` --- .github/workflows/publish.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3e8c8dc0f..f06425972 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: image-publish: - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@docker-build-secret + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.25 permissions: # for GITHUB_TOKEN packages: write with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90fe5307f..873a5bbed 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -751,7 +751,7 @@ jobs: test-run-realistic, test-run-single-pixel, ] - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/tag-and-release.yml@v1.24 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/tag-and-release.yml@v1.25 permissions: # for GITHUB_TOKEN contents: write with: From d7ff45d20ebada97872a8dd7a152b4a43be03f3c Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 8 Apr 2026 14:51:33 -0400 Subject: [PATCH 38/63] attempted formatting fixes with ruff --- skymap_scanner/recos/monopod_taupede.py | 515 ++++++++++++++---------- 1 file changed, 306 insertions(+), 209 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 3de5d9987..1938927d0 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,6 +1,6 @@ import datetime import os -from typing import Final, List +from typing import Final, List import numpy from icecube import ( # noqa: F401 @@ -14,7 +14,7 @@ millipede, photonics_service, recclasses, - simclasses + simclasses, ) @@ -26,11 +26,10 @@ from .common.pulse_proc import mask_deepcore, pulse_cleaning - import random -#IMPORTS FROM REC_TAU: +# IMPORTS FROM REC_TAU: import argparse from importlib.metadata import version @@ -47,7 +46,9 @@ from icecube import level3_filter_muon # noqa: F401 # for srt cleaning -from icecube.STTools.seededRT.configuration_services import I3DOMLinkSeededRTConfigurationService +from icecube.STTools.seededRT.configuration_services import ( + I3DOMLinkSeededRTConfigurationService, +) # for gulliver from icecube.gulliver_modules import gulliview @@ -55,23 +56,27 @@ from snowflake import library, unfold import reco from reco import skymap, dom -from reco.masks import (earlypulses, - maskdc, - maskunhits, - maskstrings, - maskdust, - pulse_cleaning) +from reco.masks import ( + earlypulses, + maskdc, + maskunhits, + maskstrings, + maskdust, + pulse_cleaning, +) from reco.truth import truth, druth -from reco.mlpd import (MonopodWrapper, - TaupedeWrapper, - MillipedeWrapper, - preferred, - define_splines) +from reco.mlpd import ( + MonopodWrapper, + TaupedeWrapper, + MillipedeWrapper, + preferred, + define_splines, +) from reco.seed import default_seeds -class MonoTau(RecoInterface): - #SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: +class MonoTau(RecoInterface): + # SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" @@ -79,8 +84,13 @@ class MonoTau(RecoInterface): FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" - SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, - FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] + SPLINE_REQUIREMENTS = [ + FTP_ABS_SPLINE, + FTP_PROB_SPLINE, + FTP_EFFD_SPLINE, + FTP_EFFP_SPLINE, + FTP_TMOD_SPLINE, + ] """Logic for a dummy reco.""" @@ -88,18 +98,15 @@ def __init__(self, realtime_format_version: str): super().__init__(realtime_format_version) self.rotate_vertex = True self.refine_time = True - #VALUE TAKEN FROM MILLIPEDE_WILKS: + # VALUE TAKEN FROM MILLIPEDE_WILKS: self.add_fallback_position = True - def get_vertex_variations() -> List[dataclasses.I3Position]: """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variation.""" return VertexGenerator.point() - - def setup_reco(self): - #SECTION TAKEN FROM MILLIPEDE_WILKS: + # SECTION TAKEN FROM MILLIPEDE_WILKS: datastager = self.get_datastager() datastager.stage_files(self.SPLINE_REQUIREMENTS) @@ -111,25 +118,25 @@ def setup_reco(self): tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) self.cascade_service = photonics_service.I3PhotoSplineService( - abs_spline, prob_spline, timingSigma=0.0, - effectivedistancetable = effd_spline, - tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), + abs_spline, + prob_spline, + timingSigma=0.0, + effectivedistancetable=effd_spline, + tiltTableDir=os.path.expandvars( + "$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/" + ), quantileEpsilon=1, - effectivedistancetableprob = effp_spline, - effectivedistancetabletmod = tmod_spline) + effectivedistancetableprob=effp_spline, + effectivedistancetabletmod=tmod_spline, + ) self.muon_service = None - - - - - @staticmethod @icetray.traysegment def prepare_frames(tray, name, logger, **kwargs) -> None: - #CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED - #Generates the vertex seed for the initial scan. + # CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED + # Generates the vertex seed for the initial scan. # Only run if HESE_VHESelfVeto is not present in the frame. # VertexThreshold is 250 in the original HESE analysis (Tianlu) # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. @@ -138,28 +145,40 @@ def extract_seed(frame): frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] - tray.Add(extract_seed, "ExtractSeed", If = lambda frame: frame.Has("HESE_VHESelfVeto")) + tray.Add( + extract_seed, "ExtractSeed", If=lambda frame: frame.Has("HESE_VHESelfVeto") + ) - tray.AddModule('VHESelfVeto', 'selfveto', + tray.AddModule( + "VHESelfVeto", + "selfveto", VertexThreshold=250, - Pulses=self.pulsesName_input+'HLC', - OutputBool='HESE_VHESelfVeto', + Pulses=self.pulsesName_input + "HLC", + OutputBool="HESE_VHESelfVeto", OutputVertexTime=cfg.INPUT_TIME_NAME, OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: "HESE_VHESelfVeto" not in frame) + If=lambda frame: "HESE_VHESelfVeto" not in frame, + ) # this only runs if the previous module did not return anything - tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', + tray.AddModule( + "VHESelfVeto", + "selfveto-emergency-lowen-settings", VertexThreshold=5, - Pulses=self.pulsesName_input+'HLC', - OutputBool='VHESelfVeto_meaningless_lowen', + Pulses=self.pulsesName_input + "HLC", + OutputBool="VHESelfVeto_meaningless_lowen", OutputVertexTime=cfg.INPUT_TIME_NAME, OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: not frame.Has("HESE_VHESelfVeto")) + If=lambda frame: not frame.Has("HESE_VHESelfVeto"), + ) - tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) + tray.Add( + mask_deepcore, + origpulses=self.pulsesName_input, + maskedpulses=self.pulsesName, + ) - #OTHER METHODS FROM MILLIPEDE_WILKS: + # OTHER METHODS FROM MILLIPEDE_WILKS: def makeSurePulsesExist(frame, pulsesName) -> None: if pulsesName not in frame: raise RuntimeError(f"{pulsesName} not in frame") @@ -170,42 +189,71 @@ def makeSurePulsesExist(frame, pulsesName) -> None: @icetray.traysegment def exclusions(self, tray, name): - tray.Add('Delete', keys=['BrightDOMs', - 'SaturatedDOMs', - 'DeepCoreDOMs', - self.pulsesName_cleaned, - self.pulsesName_cleaned+'TimeWindows', - self.pulsesName_cleaned+'TimeRange']) - - exclusionList = \ - tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', - Pulses = self.pulsesName, - ExcludeDeepCore='DeepCoreDOMs', - ExcludeSaturatedDOMs='SaturatedDOMs', - ExcludeBrightDOMs='BrightDOMs', + tray.Add( + "Delete", + keys=[ + "BrightDOMs", + "SaturatedDOMs", + "DeepCoreDOMs", + self.pulsesName_cleaned, + self.pulsesName_cleaned + "TimeWindows", + self.pulsesName_cleaned + "TimeRange", + ], + ) + + exclusionList = tray.AddSegment( + millipede.HighEnergyExclusions, + "millipede_DOM_exclusions", + Pulses=self.pulsesName, + ExcludeDeepCore="DeepCoreDOMs", + ExcludeSaturatedDOMs="SaturatedDOMs", + ExcludeBrightDOMs="BrightDOMs", BrightDOMThreshold=2, - BadDomsList='BadDomsList', - CalibrationErrata='CalibrationErrata', - SaturationWindows='SaturationWindows') - - + BadDomsList="BadDomsList", + CalibrationErrata="CalibrationErrata", + SaturationWindows="SaturationWindows", + ) - #I like having frame objects in there even if they are empty for some frames + # I like having frame objects in there even if they are empty for some frames def createEmptyDOMLists(frame, ListNames=[]): for name in ListNames: if name in frame: continue frame[name] = dataclasses.I3VectorOMKey() - tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', - ListNames = ["BrightDOMs"]) + + tray.AddModule( + createEmptyDOMLists, "createEmptyDOMLists", ListNames=["BrightDOMs"] + ) # exclude bright DOMs ExcludedDOMs = exclusionList def skipunhits(frame, output, pulses): - keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] - keepoms = list(range(1,60,5)) + keepstrings = [ + 1, + 3, + 5, + 14, + 16, + 18, + 20, + 31, + 33, + 35, + 37, + 39, + 51, + 53, + 55, + 57, + 59, + 68, + 70, + 72, + 74, + ] + keepoms = list(range(1, 60, 5)) all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame(frame, pulses) - omgeo = frame['I3Geometry'] + omgeo = frame["I3Geometry"] geo = omgeo.omgeo unhits = dataclasses.I3VectorOMKey() for k, v in geo.items(): @@ -221,39 +269,38 @@ def skipunhits(frame, output, pulses): frame[output] = unhits ################## - tray.AddModule(pulse_cleaning, "LatePulseCleaning", + tray.AddModule( + pulse_cleaning, + "LatePulseCleaning", input_pulses_name=self.pulsesName, output_pulses_name=self.pulsesName_cleaned, - residual=1.5e3*I3Units.ns) - ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') + residual=1.5e3 * I3Units.ns, + ) + ExcludedDOMs.append(self.pulsesName_cleaned + "TimeWindows") - tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) - ExcludedDOMs.append('OtherUnhits') + tray.Add(skipunhits, output="OtherUnhits", pulses=self.pulsesName_cleaned) + ExcludedDOMs.append("OtherUnhits") return ExcludedDOMs - - - - #SEPARATE METHODS FROM REC_TAU.PY: + # SEPARATE METHODS FROM REC_TAU.PY: def sane(frame, split_names): for split_name in split_names: if which_split(split_name=split_name)(frame): return True return False - - '''def print_frameid(frame): + """def print_frameid(frame): eventid = frame['I3EventHeader'].event_id - print("*******Currently processing frame %s*******" %eventid)''' - + print("*******Currently processing frame %s*******" %eventid)""" def fixed_dir(filelist, isdata, hypo, split_names, nframes=None): truths = [] def extract(frame): - truths.append(frame['cc'].dir) + truths.append(frame["cc"].dir) + tray = I3Tray() - tray.Add('I3Reader', Filenamelist=filelist) + tray.Add("I3Reader", Filenamelist=filelist) tray.Add(sane, split_names=split_names) if isdata: tray.Add(druth, hypo=hypo) @@ -266,32 +313,33 @@ def extract(frame): tray.Execute(nframes) if len(set([(_.zenith, _.azimuth) for _ in truths])) != 1: icetray.logging.log_warn( - 'The number of extracted, unique true dirs is not 1, not updating stepXYZ') + "The number of extracted, unique true dirs is not 1, not updating stepXYZ" + ) return None return truths[0] - - - @staticmethod - #TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY + # TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY @icetray.traysegment - #USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION - def traysegment(self,tray, name, logger, seed): - - #TAKEN FROM MILLIPEDE_WILKS: + # USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION + def traysegment(self, tray, name, logger, seed): + + # TAKEN FROM MILLIPEDE_WILKS: ExcludedDOMs = tray.Add(self.exclusions) tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) def check_cal(frame): - cal = frame['I3Calibration'] - logger.debug('Mean SPEs') + cal = frame["I3Calibration"] + logger.debug("Mean SPEs") for omkey in list(cal.dom_cal.keys())[::100]: x = cal.dom_cal[omkey] mean_spe = dataclasses.mean_spe_charge(x) - logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') - logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') + logger.debug(f"...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}") + logger.debug( + f"......: {x.combined_spe_charge_distribution.compensation_factor}" + ) + tray.Add(check_cal) def notify0(frame): @@ -299,97 +347,130 @@ def notify0(frame): tray.AddModule(notify0, "notify0") - - - - - - - #BEGIN REC_TAU + # BEGIN REC_TAU wrapperfn = TaupedeWrapper - specifier = 'TaupedeFit' - loss_vector_suffix = 'Particles' - #STARTING WITH THE DEFAULT ITERATIONS NUMBER + specifier = "TaupedeFit" + loss_vector_suffix = "Particles" + # STARTING WITH THE DEFAULT ITERATIONS NUMBER iterations = 2 - #TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY - #USED DEFAULT FOR SPLIT NAMES - tray.Add(sane, split_names=['InIceSplit',]) - #tray.Add(print_frameid) - - #CODE TO RUN WHEN ISDATA=NONE - tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has('cc')) - - #LEAVING OUT SEED CHAIN FOR NOW - - - - #LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS - #SETTING PULSES FOR RECO TO DEFAULT - pulses_for_reco='SplitInIcePulses' - millipede_params = {'Pulses': f'{pulses_for_reco}PulseCleaned', - 'CascadePhotonicsService': self.cascade_service, - 'MuonPhotonicsService': None, - 'ExcludedDOMs': self.excludedDOMs, - 'ReadoutWindow': f'{pulses_for_reco}PulseCleanedTimeRange', - 'PartialExclusion': True, - 'PhotonsPerBin': 0, - 'UseUnhitDOMs': not False, - 'MinTimeWidth': 16, - 'BinSigma': np.nan, - 'RelUncertainty': 0.05,'StepZenith':0,'StepAzimuth':0} - icetray.logging.log_info(pformat(millipede_params), - __name__) - minis = [_ for _ in ['MIGRAD', - 'iMIGRAD', - 'SIMPLEX', - 'iSIMPLEX', - 'LBFGSB']] - sfx='PPB0' + # TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY + # USED DEFAULT FOR SPLIT NAMES + tray.Add( + sane, + split_names=[ + "InIceSplit", + ], + ) + # tray.Add(print_frameid) + + # CODE TO RUN WHEN ISDATA=NONE + tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has("cc")) + + # LEAVING OUT SEED CHAIN FOR NOW + + # LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS + # SETTING PULSES FOR RECO TO DEFAULT + pulses_for_reco = "SplitInIcePulses" + millipede_params = { + "Pulses": f"{pulses_for_reco}PulseCleaned", + "CascadePhotonicsService": self.cascade_service, + "MuonPhotonicsService": None, + "ExcludedDOMs": self.excludedDOMs, + "ReadoutWindow": f"{pulses_for_reco}PulseCleanedTimeRange", + "PartialExclusion": True, + "PhotonsPerBin": 0, + "UseUnhitDOMs": not False, + "MinTimeWidth": 16, + "BinSigma": np.nan, + "RelUncertainty": 0.05, + "StepZenith": 0, + "StepAzimuth": 0, + } + icetray.logging.log_info(pformat(millipede_params), __name__) + minis = [_ for _ in ["MIGRAD", "iMIGRAD", "SIMPLEX", "iSIMPLEX", "LBFGSB"]] + sfx = "PPB0" for mini in minis: - - tray.Add(wrapperfn, - f'{mini}_{PPB0}', - Seed=Seed, - Minimizer=mini, - Unfold=False, - Chain=1, - Iterations=iterations, - **millipede_params) + tray.Add( + wrapperfn, + f"{mini}_{PPB0}", + Seed=Seed, + Minimizer=mini, + Unfold=False, + Chain=1, + Iterations=iterations, + **millipede_params, + ) seeder = lilliput.segments.add_seed_service( - tray, - millipede_params['Pulses'], - [f'{specifier}_{mini}_{PPB0}']) + tray, millipede_params["Pulses"], [f"{specifier}_{mini}_{PPB0}"] + ) minispec = mini.lower() - relerr=0.05 - minispec += f'.relerr{relerr:.2f}' - - prefs = [_ for tup in [[f'TaupedeFit_{mini}_{PPB0}', f'MonopodFit_{mini}_{PPB0}'] for mini in minis] for _ in tup] - tray.Add(preferred, - i3_particles_fitparams=[(_, f'{_}FitParams') for _ in prefs], - If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs])) - - - #print( prefs ) - - #print("running HESE, with printing modules") + relerr = 0.05 + minispec += f".relerr{relerr:.2f}" + + prefs = [ + _ + for tup in [ + [f"TaupedeFit_{mini}_{PPB0}", f"MonopodFit_{mini}_{PPB0}"] + for mini in minis + ] + for _ in tup + ] + tray.Add( + preferred, + i3_particles_fitparams=[(_, f"{_}FitParams") for _ in prefs], + If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs]), + ) + + # print( prefs ) + + # print("running HESE, with printing modules") from segments.MillipedeWrapper import MillipedeWrapper - + # energy definition gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" gcdfile = dataio.I3File(gcdfilepath) frame = gcdfile.pop_frame() - while 'I3Geometry' not in frame: + while "I3Geometry" not in frame: frame = gcdfile.pop_frame() - geometry = frame['I3Geometry'].omgeo - - strings = [1, 2, 3, 4, 5, 6, 13, 21, 30, 40, 50, 59, 67, 74, 73, 72, 78, 77, 76, 75, 68, 60, 51, 41, 31, 22, 14, 7] + geometry = frame["I3Geometry"].omgeo + + strings = [ + 1, + 2, + 3, + 4, + 5, + 6, + 13, + 21, + 30, + 40, + 50, + 59, + 67, + 74, + 73, + 72, + 78, + 77, + 76, + 75, + 68, + 60, + 51, + 41, + 31, + 22, + 14, + 7, + ] outerbounds = {} cx, cy = [], [] for string in strings: omkey = icetray.OMKey(string, 1) - #if geometry.has_key(omkey): + # if geometry.has_key(omkey): x, y = geometry[omkey].position.x, geometry[omkey].position.y outerbounds[string] = (x, y) cx.append(x) @@ -399,44 +480,59 @@ def notify0(frame): outeredge_x = cx[order] outeredge_y = cy[order] - #print(sfx) - - - #SHOULD I TAKE THIS OUT SINCE ITS TRACK - #track reco - tray.Add('I3OMSelection', 'omselection_HESE', - InputResponse = 'SRT' + "SplitInIcePulses", - OmittedStrings = [79,80,81,82,83,84,85,86], # deepcore strings - OutputOMSelection = f'SRTSplitInIcePulses_BadOMSelectionString_{sfx}', - OutputResponse = f"SRTSplitInIcePulses_IC_Singles_{sfx}") - - tray.Add(SPEFit, f'SPEFit16_{sfx}', - Pulses = f"SRTSplitInIcePulses_IC_Singles_{sfx}", - Iterations = 16) - - del millipede_params["PhotonsPerBin"] # also input to MillipedeWrapper next, gives error if entered twice - - - #HESE millipede - tray.Add(MillipedeWrapper, f'HESEMillipedeFit_{sfx}', - seed_cascade = f'MonopodFit_iMIGRAD_{sfx}', - seed_tau = f'TaupedeFit_iMIGRAD_{sfx}', - seed_track = f'SPEFit16_{sfx}', - PhotonsPerBin = 0, - ShowerSpacing = 5, + # print(sfx) + + # SHOULD I TAKE THIS OUT SINCE ITS TRACK + # track reco + tray.Add( + "I3OMSelection", + "omselection_HESE", + InputResponse="SRT" + "SplitInIcePulses", + OmittedStrings=[79, 80, 81, 82, 83, 84, 85, 86], # deepcore strings + OutputOMSelection=f"SRTSplitInIcePulses_BadOMSelectionString_{sfx}", + OutputResponse=f"SRTSplitInIcePulses_IC_Singles_{sfx}", + ) + + tray.Add( + SPEFit, + f"SPEFit16_{sfx}", + Pulses=f"SRTSplitInIcePulses_IC_Singles_{sfx}", + Iterations=16, + ) + + del millipede_params[ + "PhotonsPerBin" + ] # also input to MillipedeWrapper next, gives error if entered twice + + # HESE millipede + tray.Add( + MillipedeWrapper, + f"HESEMillipedeFit_{sfx}", + seed_cascade=f"MonopodFit_iMIGRAD_{sfx}", + seed_tau=f"TaupedeFit_iMIGRAD_{sfx}", + seed_track=f"SPEFit16_{sfx}", + PhotonsPerBin=0, + ShowerSpacing=5, innerboundary=550, outerboundary=650, outeredge_x=outeredge_x, outeredge_y=outeredge_y, - **millipede_params) - - - #rename - tray.Add('Rename', - Keys=['SRTSplitInIcePulses_IC_Singles', f'SRTSplitInIcePulses_IC_Singles_{sfx}', - 'PreferredFit_key', f'PreferredFit_key_{sfx}', - 'PreferredFit', f"PreferredFit_{sfx}"]) - #LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW + **millipede_params, + ) + + # rename + tray.Add( + "Rename", + Keys=[ + "SRTSplitInIcePulses_IC_Singles", + f"SRTSplitInIcePulses_IC_Singles_{sfx}", + "PreferredFit_key", + f"PreferredFit_key_{sfx}", + "PreferredFit", + f"PreferredFit_{sfx}", + ], + ) + # LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW def notify1(frame): logger.debug(f"reco complete! {datetime.datetime.now()}") @@ -454,7 +550,8 @@ def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariati posvar_id=frame[cfg.I3FRAME_POSVAR].value, position=frame["Dummy_pos"], time=frame["Dummy_time"].value, - energy=frame["Dummy_time"].value) + energy=frame["Dummy_time"].value, + ) # Provide a standard alias for the reconstruction class provided by this module. From fcb16eda9911d2288ff403463964e7ec6a5b7abd Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 8 Apr 2026 17:10:20 -0400 Subject: [PATCH 39/63] attempted format fixing with black --- skymap_scanner/recos/monopod_taupede.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 1938927d0..cddd84bb3 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -28,7 +28,6 @@ import random - # IMPORTS FROM REC_TAU: import argparse from importlib.metadata import version From c1cd0b0506c1643668d35c5f38927737e7b4d24f Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 8 Apr 2026 17:26:42 -0400 Subject: [PATCH 40/63] reverting to previous version --- skymap_scanner/recos/monopod_taupede.py | 543 ++++++++++-------------- 1 file changed, 224 insertions(+), 319 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index cddd84bb3..45a4c18f7 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,34 +1,31 @@ +from common.calculator import poisson_llh import datetime -import os -from typing import Final, List +import random +import time +from typing import List, Final -import numpy -from icecube import ( # noqa: F401 - VHESelfVeto, +from icecube.icetray import I3Units # type: ignore[import] +from icecube import ( # type: ignore[import] # noqa: F401 dataclasses, frame_object_diff, gulliver, gulliver_modules, icetray, - lilliput, millipede, photonics_service, recclasses, simclasses, ) +from icecube.icetray import I3Frame # type: ignore[import] -from icecube.icetray import I3Frame, I3Units, I3Tray - -from .. import config as cfg -from ..utils.pixel_classes import RecoPixelVariation -from . import RecoInterface, VertexGenerator -from .common.pulse_proc import mask_deepcore, pulse_cleaning - - -import random +from skymap_scanner import config as cfg +from skymap_scanner.utils.pixel_classes import RecoPixelVariation +from skymap_scanner.recos import RecoInterface, VertexGenerator +from skymap_scanner.recos.common.pulse_proc import mask_deepcore, pulse_cleaning -# IMPORTS FROM REC_TAU: +#IMPORTS FROM REC_TAU +import os import argparse from importlib.metadata import version @@ -36,6 +33,11 @@ import numpy as np from icecube import dataio +from icecube import (icetray, + dataclasses, + photonics_service, + mue) # noqa: F401 +from icecube.icetray import I3Tray, I3Units from icecube.phys_services.which_split import which_split from icecube.millipede import HighEnergyExclusions from icecube.spline_reco import SplineMPE @@ -45,37 +47,32 @@ from icecube import level3_filter_muon # noqa: F401 # for srt cleaning -from icecube.STTools.seededRT.configuration_services import ( - I3DOMLinkSeededRTConfigurationService, -) +from icecube.STTools.seededRT.configuration_services import I3DOMLinkSeededRTConfigurationService # for gulliver +from icecube import lilliput from icecube.gulliver_modules import gulliview from snowflake import library, unfold import reco from reco import skymap, dom -from reco.masks import ( - earlypulses, - maskdc, - maskunhits, - maskstrings, - maskdust, - pulse_cleaning, -) +from reco.masks import (earlypulses, + maskdc, + maskunhits, + maskstrings, + maskdust, + pulse_cleaning) from reco.truth import truth, druth -from reco.mlpd import ( - MonopodWrapper, - TaupedeWrapper, - MillipedeWrapper, - preferred, - define_splines, -) +from reco.mlpd import (MonopodWrapper, + TaupedeWrapper, + MillipedeWrapper, + preferred, + define_splines) from reco.seed import default_seeds - class MonoTau(RecoInterface): - # SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: + + #SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" @@ -83,13 +80,8 @@ class MonoTau(RecoInterface): FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" - SPLINE_REQUIREMENTS = [ - FTP_ABS_SPLINE, - FTP_PROB_SPLINE, - FTP_EFFD_SPLINE, - FTP_EFFP_SPLINE, - FTP_TMOD_SPLINE, - ] + SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, + FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] """Logic for a dummy reco.""" @@ -97,15 +89,18 @@ def __init__(self, realtime_format_version: str): super().__init__(realtime_format_version) self.rotate_vertex = True self.refine_time = True - # VALUE TAKEN FROM MILLIPEDE_WILKS: + #VALUE TAKEN FROM MILLIPEDE_WILKS: self.add_fallback_position = True + def get_vertex_variations() -> List[dataclasses.I3Position]: """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variation.""" return VertexGenerator.point() + + def setup_reco(self): - # SECTION TAKEN FROM MILLIPEDE_WILKS: + #SECTION TAKEN FROM MILLIPEDE_WILKS: datastager = self.get_datastager() datastager.stage_files(self.SPLINE_REQUIREMENTS) @@ -117,25 +112,25 @@ def setup_reco(self): tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) self.cascade_service = photonics_service.I3PhotoSplineService( - abs_spline, - prob_spline, - timingSigma=0.0, - effectivedistancetable=effd_spline, - tiltTableDir=os.path.expandvars( - "$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/" - ), + abs_spline, prob_spline, timingSigma=0.0, + effectivedistancetable = effd_spline, + tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), quantileEpsilon=1, - effectivedistancetableprob=effp_spline, - effectivedistancetabletmod=tmod_spline, - ) + effectivedistancetableprob = effp_spline, + effectivedistancetabletmod = tmod_spline) self.muon_service = None + + + + + @staticmethod @icetray.traysegment def prepare_frames(tray, name, logger, **kwargs) -> None: - # CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED - # Generates the vertex seed for the initial scan. + #CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED + #Generates the vertex seed for the initial scan. # Only run if HESE_VHESelfVeto is not present in the frame. # VertexThreshold is 250 in the original HESE analysis (Tianlu) # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. @@ -144,40 +139,28 @@ def extract_seed(frame): frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] - tray.Add( - extract_seed, "ExtractSeed", If=lambda frame: frame.Has("HESE_VHESelfVeto") - ) + tray.Add(extract_seed, "ExtractSeed", If = lambda frame: frame.Has("HESE_VHESelfVeto")) - tray.AddModule( - "VHESelfVeto", - "selfveto", + tray.AddModule('VHESelfVeto', 'selfveto', VertexThreshold=250, - Pulses=self.pulsesName_input + "HLC", - OutputBool="HESE_VHESelfVeto", + Pulses=self.pulsesName_input+'HLC', + OutputBool='HESE_VHESelfVeto', OutputVertexTime=cfg.INPUT_TIME_NAME, OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: "HESE_VHESelfVeto" not in frame, - ) + If=lambda frame: "HESE_VHESelfVeto" not in frame) # this only runs if the previous module did not return anything - tray.AddModule( - "VHESelfVeto", - "selfveto-emergency-lowen-settings", + tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', VertexThreshold=5, - Pulses=self.pulsesName_input + "HLC", - OutputBool="VHESelfVeto_meaningless_lowen", + Pulses=self.pulsesName_input+'HLC', + OutputBool='VHESelfVeto_meaningless_lowen', OutputVertexTime=cfg.INPUT_TIME_NAME, OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: not frame.Has("HESE_VHESelfVeto"), - ) + If=lambda frame: not frame.Has("HESE_VHESelfVeto")) - tray.Add( - mask_deepcore, - origpulses=self.pulsesName_input, - maskedpulses=self.pulsesName, - ) + tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) - # OTHER METHODS FROM MILLIPEDE_WILKS: + #OTHER METHODS FROM MILLIPEDE_WILKS: def makeSurePulsesExist(frame, pulsesName) -> None: if pulsesName not in frame: raise RuntimeError(f"{pulsesName} not in frame") @@ -188,71 +171,42 @@ def makeSurePulsesExist(frame, pulsesName) -> None: @icetray.traysegment def exclusions(self, tray, name): - tray.Add( - "Delete", - keys=[ - "BrightDOMs", - "SaturatedDOMs", - "DeepCoreDOMs", - self.pulsesName_cleaned, - self.pulsesName_cleaned + "TimeWindows", - self.pulsesName_cleaned + "TimeRange", - ], - ) - - exclusionList = tray.AddSegment( - millipede.HighEnergyExclusions, - "millipede_DOM_exclusions", - Pulses=self.pulsesName, - ExcludeDeepCore="DeepCoreDOMs", - ExcludeSaturatedDOMs="SaturatedDOMs", - ExcludeBrightDOMs="BrightDOMs", + tray.Add('Delete', keys=['BrightDOMs', + 'SaturatedDOMs', + 'DeepCoreDOMs', + self.pulsesName_cleaned, + self.pulsesName_cleaned+'TimeWindows', + self.pulsesName_cleaned+'TimeRange']) + + exclusionList = \ + tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', + Pulses = self.pulsesName, + ExcludeDeepCore='DeepCoreDOMs', + ExcludeSaturatedDOMs='SaturatedDOMs', + ExcludeBrightDOMs='BrightDOMs', BrightDOMThreshold=2, - BadDomsList="BadDomsList", - CalibrationErrata="CalibrationErrata", - SaturationWindows="SaturationWindows", - ) + BadDomsList='BadDomsList', + CalibrationErrata='CalibrationErrata', + SaturationWindows='SaturationWindows') - # I like having frame objects in there even if they are empty for some frames + + + #I like having frame objects in there even if they are empty for some frames def createEmptyDOMLists(frame, ListNames=[]): for name in ListNames: if name in frame: continue frame[name] = dataclasses.I3VectorOMKey() - - tray.AddModule( - createEmptyDOMLists, "createEmptyDOMLists", ListNames=["BrightDOMs"] - ) + tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', + ListNames = ["BrightDOMs"]) # exclude bright DOMs ExcludedDOMs = exclusionList def skipunhits(frame, output, pulses): - keepstrings = [ - 1, - 3, - 5, - 14, - 16, - 18, - 20, - 31, - 33, - 35, - 37, - 39, - 51, - 53, - 55, - 57, - 59, - 68, - 70, - 72, - 74, - ] - keepoms = list(range(1, 60, 5)) + keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] + keepoms = list(range(1,60,5)) all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame(frame, pulses) - omgeo = frame["I3Geometry"] + omgeo = frame['I3Geometry'] geo = omgeo.omgeo unhits = dataclasses.I3VectorOMKey() for k, v in geo.items(): @@ -268,38 +222,39 @@ def skipunhits(frame, output, pulses): frame[output] = unhits ################## - tray.AddModule( - pulse_cleaning, - "LatePulseCleaning", + tray.AddModule(pulse_cleaning, "LatePulseCleaning", input_pulses_name=self.pulsesName, output_pulses_name=self.pulsesName_cleaned, - residual=1.5e3 * I3Units.ns, - ) - ExcludedDOMs.append(self.pulsesName_cleaned + "TimeWindows") + residual=1.5e3*I3Units.ns) + ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') - tray.Add(skipunhits, output="OtherUnhits", pulses=self.pulsesName_cleaned) - ExcludedDOMs.append("OtherUnhits") + tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) + ExcludedDOMs.append('OtherUnhits') return ExcludedDOMs - # SEPARATE METHODS FROM REC_TAU.PY: + + + + #SEPARATE METHODS FROM REC_TAU.PY: def sane(frame, split_names): for split_name in split_names: if which_split(split_name=split_name)(frame): return True return False - """def print_frameid(frame): + + '''def print_frameid(frame): eventid = frame['I3EventHeader'].event_id - print("*******Currently processing frame %s*******" %eventid)""" + print("*******Currently processing frame %s*******" %eventid)''' + def fixed_dir(filelist, isdata, hypo, split_names, nframes=None): truths = [] def extract(frame): - truths.append(frame["cc"].dir) - + truths.append(frame['cc'].dir) tray = I3Tray() - tray.Add("I3Reader", Filenamelist=filelist) + tray.Add('I3Reader', Filenamelist=filelist) tray.Add(sane, split_names=split_names) if isdata: tray.Add(druth, hypo=hypo) @@ -312,33 +267,32 @@ def extract(frame): tray.Execute(nframes) if len(set([(_.zenith, _.azimuth) for _ in truths])) != 1: icetray.logging.log_warn( - "The number of extracted, unique true dirs is not 1, not updating stepXYZ" - ) + 'The number of extracted, unique true dirs is not 1, not updating stepXYZ') return None return truths[0] + + + @staticmethod - # TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY + #TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY @icetray.traysegment - # USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION - def traysegment(self, tray, name, logger, seed): - - # TAKEN FROM MILLIPEDE_WILKS: + #USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION + def traysegment(self,tray, name, logger, seed): + + #TAKEN FROM MILLIPEDE_WILKS: ExcludedDOMs = tray.Add(self.exclusions) tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) def check_cal(frame): - cal = frame["I3Calibration"] - logger.debug("Mean SPEs") + cal = frame['I3Calibration'] + logger.debug('Mean SPEs') for omkey in list(cal.dom_cal.keys())[::100]: x = cal.dom_cal[omkey] mean_spe = dataclasses.mean_spe_charge(x) - logger.debug(f"...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}") - logger.debug( - f"......: {x.combined_spe_charge_distribution.compensation_factor}" - ) - + logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') + logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') tray.Add(check_cal) def notify0(frame): @@ -346,130 +300,97 @@ def notify0(frame): tray.AddModule(notify0, "notify0") - # BEGIN REC_TAU + + + + + + + #BEGIN REC_TAU wrapperfn = TaupedeWrapper - specifier = "TaupedeFit" - loss_vector_suffix = "Particles" - # STARTING WITH THE DEFAULT ITERATIONS NUMBER + specifier = 'TaupedeFit' + loss_vector_suffix = 'Particles' + #STARTING WITH THE DEFAULT ITERATIONS NUMBER iterations = 2 - # TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY - # USED DEFAULT FOR SPLIT NAMES - tray.Add( - sane, - split_names=[ - "InIceSplit", - ], - ) - # tray.Add(print_frameid) - - # CODE TO RUN WHEN ISDATA=NONE - tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has("cc")) - - # LEAVING OUT SEED CHAIN FOR NOW - - # LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS - # SETTING PULSES FOR RECO TO DEFAULT - pulses_for_reco = "SplitInIcePulses" - millipede_params = { - "Pulses": f"{pulses_for_reco}PulseCleaned", - "CascadePhotonicsService": self.cascade_service, - "MuonPhotonicsService": None, - "ExcludedDOMs": self.excludedDOMs, - "ReadoutWindow": f"{pulses_for_reco}PulseCleanedTimeRange", - "PartialExclusion": True, - "PhotonsPerBin": 0, - "UseUnhitDOMs": not False, - "MinTimeWidth": 16, - "BinSigma": np.nan, - "RelUncertainty": 0.05, - "StepZenith": 0, - "StepAzimuth": 0, - } - icetray.logging.log_info(pformat(millipede_params), __name__) - minis = [_ for _ in ["MIGRAD", "iMIGRAD", "SIMPLEX", "iSIMPLEX", "LBFGSB"]] - sfx = "PPB0" + #TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY + #USED DEFAULT FOR SPLIT NAMES + tray.Add(sane, split_names=['InIceSplit',]) + #tray.Add(print_frameid) + + #CODE TO RUN WHEN ISDATA=NONE + tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has('cc')) + + #LEAVING OUT SEED CHAIN FOR NOW + + + + #LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS + #SETTING PULSES FOR RECO TO DEFAULT + pulses_for_reco='SplitInIcePulses' + millipede_params = {'Pulses': f'{pulses_for_reco}PulseCleaned', + 'CascadePhotonicsService': self.cascade_service, + 'MuonPhotonicsService': None, + 'ExcludedDOMs': self.excludedDOMs, + 'ReadoutWindow': f'{pulses_for_reco}PulseCleanedTimeRange', + 'PartialExclusion': True, + 'PhotonsPerBin': 0, + 'UseUnhitDOMs': not False, + 'MinTimeWidth': 16, + 'BinSigma': np.nan, + 'RelUncertainty': 0.05,'StepZenith':0,'StepAzimuth':0} + icetray.logging.log_info(pformat(millipede_params), + __name__) + minis = [_ for _ in ['MIGRAD', + 'iMIGRAD', + 'SIMPLEX', + 'iSIMPLEX', + 'LBFGSB']] + sfx='PPB0' for mini in minis: - tray.Add( - wrapperfn, - f"{mini}_{PPB0}", - Seed=Seed, - Minimizer=mini, - Unfold=False, - Chain=1, - Iterations=iterations, - **millipede_params, - ) + + tray.Add(wrapperfn, + f'{mini}_{PPB0}', + Seed=Seed, + Minimizer=mini, + Unfold=False, + Chain=1, + Iterations=iterations, + **millipede_params) seeder = lilliput.segments.add_seed_service( - tray, millipede_params["Pulses"], [f"{specifier}_{mini}_{PPB0}"] - ) + tray, + millipede_params['Pulses'], + [f'{specifier}_{mini}_{PPB0}']) minispec = mini.lower() - relerr = 0.05 - minispec += f".relerr{relerr:.2f}" - - prefs = [ - _ - for tup in [ - [f"TaupedeFit_{mini}_{PPB0}", f"MonopodFit_{mini}_{PPB0}"] - for mini in minis - ] - for _ in tup - ] - tray.Add( - preferred, - i3_particles_fitparams=[(_, f"{_}FitParams") for _ in prefs], - If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs]), - ) - - # print( prefs ) - - # print("running HESE, with printing modules") - from segments.MillipedeWrapper import MillipedeWrapper + relerr=0.05 + minispec += f'.relerr{relerr:.2f}' + prefs = [_ for tup in [[f'TaupedeFit_{mini}_{PPB0}', f'MonopodFit_{mini}_{PPB0}'] for mini in minis] for _ in tup] + tray.Add(preferred, + i3_particles_fitparams=[(_, f'{_}FitParams') for _ in prefs], + If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs])) + + + #print( prefs ) + + #print("running HESE, with printing modules") + from segments.MillipedeWrapper import MillipedeWrapper + # energy definition gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" gcdfile = dataio.I3File(gcdfilepath) frame = gcdfile.pop_frame() - while "I3Geometry" not in frame: + while 'I3Geometry' not in frame: frame = gcdfile.pop_frame() - geometry = frame["I3Geometry"].omgeo - - strings = [ - 1, - 2, - 3, - 4, - 5, - 6, - 13, - 21, - 30, - 40, - 50, - 59, - 67, - 74, - 73, - 72, - 78, - 77, - 76, - 75, - 68, - 60, - 51, - 41, - 31, - 22, - 14, - 7, - ] + geometry = frame['I3Geometry'].omgeo + + strings = [1, 2, 3, 4, 5, 6, 13, 21, 30, 40, 50, 59, 67, 74, 73, 72, 78, 77, 76, 75, 68, 60, 51, 41, 31, 22, 14, 7] outerbounds = {} cx, cy = [], [] for string in strings: omkey = icetray.OMKey(string, 1) - # if geometry.has_key(omkey): + #if geometry.has_key(omkey): x, y = geometry[omkey].position.x, geometry[omkey].position.y outerbounds[string] = (x, y) cx.append(x) @@ -479,59 +400,44 @@ def notify0(frame): outeredge_x = cx[order] outeredge_y = cy[order] - # print(sfx) - - # SHOULD I TAKE THIS OUT SINCE ITS TRACK - # track reco - tray.Add( - "I3OMSelection", - "omselection_HESE", - InputResponse="SRT" + "SplitInIcePulses", - OmittedStrings=[79, 80, 81, 82, 83, 84, 85, 86], # deepcore strings - OutputOMSelection=f"SRTSplitInIcePulses_BadOMSelectionString_{sfx}", - OutputResponse=f"SRTSplitInIcePulses_IC_Singles_{sfx}", - ) - - tray.Add( - SPEFit, - f"SPEFit16_{sfx}", - Pulses=f"SRTSplitInIcePulses_IC_Singles_{sfx}", - Iterations=16, - ) - - del millipede_params[ - "PhotonsPerBin" - ] # also input to MillipedeWrapper next, gives error if entered twice - - # HESE millipede - tray.Add( - MillipedeWrapper, - f"HESEMillipedeFit_{sfx}", - seed_cascade=f"MonopodFit_iMIGRAD_{sfx}", - seed_tau=f"TaupedeFit_iMIGRAD_{sfx}", - seed_track=f"SPEFit16_{sfx}", - PhotonsPerBin=0, - ShowerSpacing=5, + #print(sfx) + + + #SHOULD I TAKE THIS OUT SINCE ITS TRACK + #track reco + tray.Add('I3OMSelection', 'omselection_HESE', + InputResponse = 'SRT' + "SplitInIcePulses", + OmittedStrings = [79,80,81,82,83,84,85,86], # deepcore strings + OutputOMSelection = f'SRTSplitInIcePulses_BadOMSelectionString_{sfx}', + OutputResponse = f"SRTSplitInIcePulses_IC_Singles_{sfx}") + + tray.Add(SPEFit, f'SPEFit16_{sfx}', + Pulses = f"SRTSplitInIcePulses_IC_Singles_{sfx}", + Iterations = 16) + + del millipede_params["PhotonsPerBin"] # also input to MillipedeWrapper next, gives error if entered twice + + + #HESE millipede + tray.Add(MillipedeWrapper, f'HESEMillipedeFit_{sfx}', + seed_cascade = f'MonopodFit_iMIGRAD_{sfx}', + seed_tau = f'TaupedeFit_iMIGRAD_{sfx}', + seed_track = f'SPEFit16_{sfx}', + PhotonsPerBin = 0, + ShowerSpacing = 5, innerboundary=550, outerboundary=650, outeredge_x=outeredge_x, outeredge_y=outeredge_y, - **millipede_params, - ) - - # rename - tray.Add( - "Rename", - Keys=[ - "SRTSplitInIcePulses_IC_Singles", - f"SRTSplitInIcePulses_IC_Singles_{sfx}", - "PreferredFit_key", - f"PreferredFit_key_{sfx}", - "PreferredFit", - f"PreferredFit_{sfx}", - ], - ) - # LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW + **millipede_params) + + + #rename + tray.Add('Rename', + Keys=['SRTSplitInIcePulses_IC_Singles', f'SRTSplitInIcePulses_IC_Singles_{sfx}', + 'PreferredFit_key', f'PreferredFit_key_{sfx}', + 'PreferredFit', f"PreferredFit_{sfx}"]) + #LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW def notify1(frame): logger.debug(f"reco complete! {datetime.datetime.now()}") @@ -549,8 +455,7 @@ def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariati posvar_id=frame[cfg.I3FRAME_POSVAR].value, position=frame["Dummy_pos"], time=frame["Dummy_time"].value, - energy=frame["Dummy_time"].value, - ) + energy=frame["Dummy_time"].value) # Provide a standard alias for the reconstruction class provided by this module. From 2621c0163bc9843e4d8e7eeedd04cd7c43577157 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 8 Apr 2026 18:52:08 -0400 Subject: [PATCH 41/63] running ruff format on old version --- skymap_scanner/recos/monopod_taupede.py | 515 ++++++++++++++---------- 1 file changed, 305 insertions(+), 210 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 45a4c18f7..4a06455a2 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -24,7 +24,7 @@ from skymap_scanner.recos import RecoInterface, VertexGenerator from skymap_scanner.recos.common.pulse_proc import mask_deepcore, pulse_cleaning -#IMPORTS FROM REC_TAU +# IMPORTS FROM REC_TAU import os import argparse from importlib.metadata import version @@ -33,10 +33,7 @@ import numpy as np from icecube import dataio -from icecube import (icetray, - dataclasses, - photonics_service, - mue) # noqa: F401 +from icecube import icetray, dataclasses, photonics_service, mue # noqa: F401 from icecube.icetray import I3Tray, I3Units from icecube.phys_services.which_split import which_split from icecube.millipede import HighEnergyExclusions @@ -47,7 +44,9 @@ from icecube import level3_filter_muon # noqa: F401 # for srt cleaning -from icecube.STTools.seededRT.configuration_services import I3DOMLinkSeededRTConfigurationService +from icecube.STTools.seededRT.configuration_services import ( + I3DOMLinkSeededRTConfigurationService, +) # for gulliver from icecube import lilliput @@ -56,23 +55,27 @@ from snowflake import library, unfold import reco from reco import skymap, dom -from reco.masks import (earlypulses, - maskdc, - maskunhits, - maskstrings, - maskdust, - pulse_cleaning) +from reco.masks import ( + earlypulses, + maskdc, + maskunhits, + maskstrings, + maskdust, + pulse_cleaning, +) from reco.truth import truth, druth -from reco.mlpd import (MonopodWrapper, - TaupedeWrapper, - MillipedeWrapper, - preferred, - define_splines) +from reco.mlpd import ( + MonopodWrapper, + TaupedeWrapper, + MillipedeWrapper, + preferred, + define_splines, +) from reco.seed import default_seeds -class MonoTau(RecoInterface): - #SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: +class MonoTau(RecoInterface): + # SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" @@ -80,8 +83,13 @@ class MonoTau(RecoInterface): FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" - SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, - FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] + SPLINE_REQUIREMENTS = [ + FTP_ABS_SPLINE, + FTP_PROB_SPLINE, + FTP_EFFD_SPLINE, + FTP_EFFP_SPLINE, + FTP_TMOD_SPLINE, + ] """Logic for a dummy reco.""" @@ -89,18 +97,15 @@ def __init__(self, realtime_format_version: str): super().__init__(realtime_format_version) self.rotate_vertex = True self.refine_time = True - #VALUE TAKEN FROM MILLIPEDE_WILKS: + # VALUE TAKEN FROM MILLIPEDE_WILKS: self.add_fallback_position = True - def get_vertex_variations() -> List[dataclasses.I3Position]: """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variation.""" return VertexGenerator.point() - - def setup_reco(self): - #SECTION TAKEN FROM MILLIPEDE_WILKS: + # SECTION TAKEN FROM MILLIPEDE_WILKS: datastager = self.get_datastager() datastager.stage_files(self.SPLINE_REQUIREMENTS) @@ -112,25 +117,25 @@ def setup_reco(self): tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) self.cascade_service = photonics_service.I3PhotoSplineService( - abs_spline, prob_spline, timingSigma=0.0, - effectivedistancetable = effd_spline, - tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), + abs_spline, + prob_spline, + timingSigma=0.0, + effectivedistancetable=effd_spline, + tiltTableDir=os.path.expandvars( + "$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/" + ), quantileEpsilon=1, - effectivedistancetableprob = effp_spline, - effectivedistancetabletmod = tmod_spline) + effectivedistancetableprob=effp_spline, + effectivedistancetabletmod=tmod_spline, + ) self.muon_service = None - - - - - @staticmethod @icetray.traysegment def prepare_frames(tray, name, logger, **kwargs) -> None: - #CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED - #Generates the vertex seed for the initial scan. + # CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED + # Generates the vertex seed for the initial scan. # Only run if HESE_VHESelfVeto is not present in the frame. # VertexThreshold is 250 in the original HESE analysis (Tianlu) # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. @@ -139,28 +144,40 @@ def extract_seed(frame): frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] - tray.Add(extract_seed, "ExtractSeed", If = lambda frame: frame.Has("HESE_VHESelfVeto")) + tray.Add( + extract_seed, "ExtractSeed", If=lambda frame: frame.Has("HESE_VHESelfVeto") + ) - tray.AddModule('VHESelfVeto', 'selfveto', + tray.AddModule( + "VHESelfVeto", + "selfveto", VertexThreshold=250, - Pulses=self.pulsesName_input+'HLC', - OutputBool='HESE_VHESelfVeto', + Pulses=self.pulsesName_input + "HLC", + OutputBool="HESE_VHESelfVeto", OutputVertexTime=cfg.INPUT_TIME_NAME, OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: "HESE_VHESelfVeto" not in frame) + If=lambda frame: "HESE_VHESelfVeto" not in frame, + ) # this only runs if the previous module did not return anything - tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', + tray.AddModule( + "VHESelfVeto", + "selfveto-emergency-lowen-settings", VertexThreshold=5, - Pulses=self.pulsesName_input+'HLC', - OutputBool='VHESelfVeto_meaningless_lowen', + Pulses=self.pulsesName_input + "HLC", + OutputBool="VHESelfVeto_meaningless_lowen", OutputVertexTime=cfg.INPUT_TIME_NAME, OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: not frame.Has("HESE_VHESelfVeto")) + If=lambda frame: not frame.Has("HESE_VHESelfVeto"), + ) - tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) + tray.Add( + mask_deepcore, + origpulses=self.pulsesName_input, + maskedpulses=self.pulsesName, + ) - #OTHER METHODS FROM MILLIPEDE_WILKS: + # OTHER METHODS FROM MILLIPEDE_WILKS: def makeSurePulsesExist(frame, pulsesName) -> None: if pulsesName not in frame: raise RuntimeError(f"{pulsesName} not in frame") @@ -171,42 +188,71 @@ def makeSurePulsesExist(frame, pulsesName) -> None: @icetray.traysegment def exclusions(self, tray, name): - tray.Add('Delete', keys=['BrightDOMs', - 'SaturatedDOMs', - 'DeepCoreDOMs', - self.pulsesName_cleaned, - self.pulsesName_cleaned+'TimeWindows', - self.pulsesName_cleaned+'TimeRange']) - - exclusionList = \ - tray.AddSegment(millipede.HighEnergyExclusions, 'millipede_DOM_exclusions', - Pulses = self.pulsesName, - ExcludeDeepCore='DeepCoreDOMs', - ExcludeSaturatedDOMs='SaturatedDOMs', - ExcludeBrightDOMs='BrightDOMs', + tray.Add( + "Delete", + keys=[ + "BrightDOMs", + "SaturatedDOMs", + "DeepCoreDOMs", + self.pulsesName_cleaned, + self.pulsesName_cleaned + "TimeWindows", + self.pulsesName_cleaned + "TimeRange", + ], + ) + + exclusionList = tray.AddSegment( + millipede.HighEnergyExclusions, + "millipede_DOM_exclusions", + Pulses=self.pulsesName, + ExcludeDeepCore="DeepCoreDOMs", + ExcludeSaturatedDOMs="SaturatedDOMs", + ExcludeBrightDOMs="BrightDOMs", BrightDOMThreshold=2, - BadDomsList='BadDomsList', - CalibrationErrata='CalibrationErrata', - SaturationWindows='SaturationWindows') - + BadDomsList="BadDomsList", + CalibrationErrata="CalibrationErrata", + SaturationWindows="SaturationWindows", + ) - - #I like having frame objects in there even if they are empty for some frames + # I like having frame objects in there even if they are empty for some frames def createEmptyDOMLists(frame, ListNames=[]): for name in ListNames: if name in frame: continue frame[name] = dataclasses.I3VectorOMKey() - tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', - ListNames = ["BrightDOMs"]) + + tray.AddModule( + createEmptyDOMLists, "createEmptyDOMLists", ListNames=["BrightDOMs"] + ) # exclude bright DOMs ExcludedDOMs = exclusionList def skipunhits(frame, output, pulses): - keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] - keepoms = list(range(1,60,5)) + keepstrings = [ + 1, + 3, + 5, + 14, + 16, + 18, + 20, + 31, + 33, + 35, + 37, + 39, + 51, + 53, + 55, + 57, + 59, + 68, + 70, + 72, + 74, + ] + keepoms = list(range(1, 60, 5)) all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame(frame, pulses) - omgeo = frame['I3Geometry'] + omgeo = frame["I3Geometry"] geo = omgeo.omgeo unhits = dataclasses.I3VectorOMKey() for k, v in geo.items(): @@ -222,39 +268,38 @@ def skipunhits(frame, output, pulses): frame[output] = unhits ################## - tray.AddModule(pulse_cleaning, "LatePulseCleaning", + tray.AddModule( + pulse_cleaning, + "LatePulseCleaning", input_pulses_name=self.pulsesName, output_pulses_name=self.pulsesName_cleaned, - residual=1.5e3*I3Units.ns) - ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') + residual=1.5e3 * I3Units.ns, + ) + ExcludedDOMs.append(self.pulsesName_cleaned + "TimeWindows") - tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) - ExcludedDOMs.append('OtherUnhits') + tray.Add(skipunhits, output="OtherUnhits", pulses=self.pulsesName_cleaned) + ExcludedDOMs.append("OtherUnhits") return ExcludedDOMs - - - - #SEPARATE METHODS FROM REC_TAU.PY: + # SEPARATE METHODS FROM REC_TAU.PY: def sane(frame, split_names): for split_name in split_names: if which_split(split_name=split_name)(frame): return True return False - - '''def print_frameid(frame): + """def print_frameid(frame): eventid = frame['I3EventHeader'].event_id - print("*******Currently processing frame %s*******" %eventid)''' - + print("*******Currently processing frame %s*******" %eventid)""" def fixed_dir(filelist, isdata, hypo, split_names, nframes=None): truths = [] def extract(frame): - truths.append(frame['cc'].dir) + truths.append(frame["cc"].dir) + tray = I3Tray() - tray.Add('I3Reader', Filenamelist=filelist) + tray.Add("I3Reader", Filenamelist=filelist) tray.Add(sane, split_names=split_names) if isdata: tray.Add(druth, hypo=hypo) @@ -267,32 +312,33 @@ def extract(frame): tray.Execute(nframes) if len(set([(_.zenith, _.azimuth) for _ in truths])) != 1: icetray.logging.log_warn( - 'The number of extracted, unique true dirs is not 1, not updating stepXYZ') + "The number of extracted, unique true dirs is not 1, not updating stepXYZ" + ) return None return truths[0] - - - @staticmethod - #TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY + # TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY @icetray.traysegment - #USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION - def traysegment(self,tray, name, logger, seed): - - #TAKEN FROM MILLIPEDE_WILKS: + # USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION + def traysegment(self, tray, name, logger, seed): + + # TAKEN FROM MILLIPEDE_WILKS: ExcludedDOMs = tray.Add(self.exclusions) tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) def check_cal(frame): - cal = frame['I3Calibration'] - logger.debug('Mean SPEs') + cal = frame["I3Calibration"] + logger.debug("Mean SPEs") for omkey in list(cal.dom_cal.keys())[::100]: x = cal.dom_cal[omkey] mean_spe = dataclasses.mean_spe_charge(x) - logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') - logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') + logger.debug(f"...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}") + logger.debug( + f"......: {x.combined_spe_charge_distribution.compensation_factor}" + ) + tray.Add(check_cal) def notify0(frame): @@ -300,97 +346,130 @@ def notify0(frame): tray.AddModule(notify0, "notify0") - - - - - - - #BEGIN REC_TAU + # BEGIN REC_TAU wrapperfn = TaupedeWrapper - specifier = 'TaupedeFit' - loss_vector_suffix = 'Particles' - #STARTING WITH THE DEFAULT ITERATIONS NUMBER + specifier = "TaupedeFit" + loss_vector_suffix = "Particles" + # STARTING WITH THE DEFAULT ITERATIONS NUMBER iterations = 2 - #TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY - #USED DEFAULT FOR SPLIT NAMES - tray.Add(sane, split_names=['InIceSplit',]) - #tray.Add(print_frameid) - - #CODE TO RUN WHEN ISDATA=NONE - tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has('cc')) - - #LEAVING OUT SEED CHAIN FOR NOW - - - - #LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS - #SETTING PULSES FOR RECO TO DEFAULT - pulses_for_reco='SplitInIcePulses' - millipede_params = {'Pulses': f'{pulses_for_reco}PulseCleaned', - 'CascadePhotonicsService': self.cascade_service, - 'MuonPhotonicsService': None, - 'ExcludedDOMs': self.excludedDOMs, - 'ReadoutWindow': f'{pulses_for_reco}PulseCleanedTimeRange', - 'PartialExclusion': True, - 'PhotonsPerBin': 0, - 'UseUnhitDOMs': not False, - 'MinTimeWidth': 16, - 'BinSigma': np.nan, - 'RelUncertainty': 0.05,'StepZenith':0,'StepAzimuth':0} - icetray.logging.log_info(pformat(millipede_params), - __name__) - minis = [_ for _ in ['MIGRAD', - 'iMIGRAD', - 'SIMPLEX', - 'iSIMPLEX', - 'LBFGSB']] - sfx='PPB0' + # TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY + # USED DEFAULT FOR SPLIT NAMES + tray.Add( + sane, + split_names=[ + "InIceSplit", + ], + ) + # tray.Add(print_frameid) + + # CODE TO RUN WHEN ISDATA=NONE + tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has("cc")) + + # LEAVING OUT SEED CHAIN FOR NOW + + # LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS + # SETTING PULSES FOR RECO TO DEFAULT + pulses_for_reco = "SplitInIcePulses" + millipede_params = { + "Pulses": f"{pulses_for_reco}PulseCleaned", + "CascadePhotonicsService": self.cascade_service, + "MuonPhotonicsService": None, + "ExcludedDOMs": self.excludedDOMs, + "ReadoutWindow": f"{pulses_for_reco}PulseCleanedTimeRange", + "PartialExclusion": True, + "PhotonsPerBin": 0, + "UseUnhitDOMs": not False, + "MinTimeWidth": 16, + "BinSigma": np.nan, + "RelUncertainty": 0.05, + "StepZenith": 0, + "StepAzimuth": 0, + } + icetray.logging.log_info(pformat(millipede_params), __name__) + minis = [_ for _ in ["MIGRAD", "iMIGRAD", "SIMPLEX", "iSIMPLEX", "LBFGSB"]] + sfx = "PPB0" for mini in minis: - - tray.Add(wrapperfn, - f'{mini}_{PPB0}', - Seed=Seed, - Minimizer=mini, - Unfold=False, - Chain=1, - Iterations=iterations, - **millipede_params) + tray.Add( + wrapperfn, + f"{mini}_{PPB0}", + Seed=Seed, + Minimizer=mini, + Unfold=False, + Chain=1, + Iterations=iterations, + **millipede_params, + ) seeder = lilliput.segments.add_seed_service( - tray, - millipede_params['Pulses'], - [f'{specifier}_{mini}_{PPB0}']) + tray, millipede_params["Pulses"], [f"{specifier}_{mini}_{PPB0}"] + ) minispec = mini.lower() - relerr=0.05 - minispec += f'.relerr{relerr:.2f}' - - prefs = [_ for tup in [[f'TaupedeFit_{mini}_{PPB0}', f'MonopodFit_{mini}_{PPB0}'] for mini in minis] for _ in tup] - tray.Add(preferred, - i3_particles_fitparams=[(_, f'{_}FitParams') for _ in prefs], - If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs])) - - - #print( prefs ) - - #print("running HESE, with printing modules") + relerr = 0.05 + minispec += f".relerr{relerr:.2f}" + + prefs = [ + _ + for tup in [ + [f"TaupedeFit_{mini}_{PPB0}", f"MonopodFit_{mini}_{PPB0}"] + for mini in minis + ] + for _ in tup + ] + tray.Add( + preferred, + i3_particles_fitparams=[(_, f"{_}FitParams") for _ in prefs], + If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs]), + ) + + # print( prefs ) + + # print("running HESE, with printing modules") from segments.MillipedeWrapper import MillipedeWrapper - + # energy definition gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" gcdfile = dataio.I3File(gcdfilepath) frame = gcdfile.pop_frame() - while 'I3Geometry' not in frame: + while "I3Geometry" not in frame: frame = gcdfile.pop_frame() - geometry = frame['I3Geometry'].omgeo - - strings = [1, 2, 3, 4, 5, 6, 13, 21, 30, 40, 50, 59, 67, 74, 73, 72, 78, 77, 76, 75, 68, 60, 51, 41, 31, 22, 14, 7] + geometry = frame["I3Geometry"].omgeo + + strings = [ + 1, + 2, + 3, + 4, + 5, + 6, + 13, + 21, + 30, + 40, + 50, + 59, + 67, + 74, + 73, + 72, + 78, + 77, + 76, + 75, + 68, + 60, + 51, + 41, + 31, + 22, + 14, + 7, + ] outerbounds = {} cx, cy = [], [] for string in strings: omkey = icetray.OMKey(string, 1) - #if geometry.has_key(omkey): + # if geometry.has_key(omkey): x, y = geometry[omkey].position.x, geometry[omkey].position.y outerbounds[string] = (x, y) cx.append(x) @@ -400,44 +479,59 @@ def notify0(frame): outeredge_x = cx[order] outeredge_y = cy[order] - #print(sfx) - - - #SHOULD I TAKE THIS OUT SINCE ITS TRACK - #track reco - tray.Add('I3OMSelection', 'omselection_HESE', - InputResponse = 'SRT' + "SplitInIcePulses", - OmittedStrings = [79,80,81,82,83,84,85,86], # deepcore strings - OutputOMSelection = f'SRTSplitInIcePulses_BadOMSelectionString_{sfx}', - OutputResponse = f"SRTSplitInIcePulses_IC_Singles_{sfx}") - - tray.Add(SPEFit, f'SPEFit16_{sfx}', - Pulses = f"SRTSplitInIcePulses_IC_Singles_{sfx}", - Iterations = 16) - - del millipede_params["PhotonsPerBin"] # also input to MillipedeWrapper next, gives error if entered twice - - - #HESE millipede - tray.Add(MillipedeWrapper, f'HESEMillipedeFit_{sfx}', - seed_cascade = f'MonopodFit_iMIGRAD_{sfx}', - seed_tau = f'TaupedeFit_iMIGRAD_{sfx}', - seed_track = f'SPEFit16_{sfx}', - PhotonsPerBin = 0, - ShowerSpacing = 5, + # print(sfx) + + # SHOULD I TAKE THIS OUT SINCE ITS TRACK + # track reco + tray.Add( + "I3OMSelection", + "omselection_HESE", + InputResponse="SRT" + "SplitInIcePulses", + OmittedStrings=[79, 80, 81, 82, 83, 84, 85, 86], # deepcore strings + OutputOMSelection=f"SRTSplitInIcePulses_BadOMSelectionString_{sfx}", + OutputResponse=f"SRTSplitInIcePulses_IC_Singles_{sfx}", + ) + + tray.Add( + SPEFit, + f"SPEFit16_{sfx}", + Pulses=f"SRTSplitInIcePulses_IC_Singles_{sfx}", + Iterations=16, + ) + + del millipede_params[ + "PhotonsPerBin" + ] # also input to MillipedeWrapper next, gives error if entered twice + + # HESE millipede + tray.Add( + MillipedeWrapper, + f"HESEMillipedeFit_{sfx}", + seed_cascade=f"MonopodFit_iMIGRAD_{sfx}", + seed_tau=f"TaupedeFit_iMIGRAD_{sfx}", + seed_track=f"SPEFit16_{sfx}", + PhotonsPerBin=0, + ShowerSpacing=5, innerboundary=550, outerboundary=650, outeredge_x=outeredge_x, outeredge_y=outeredge_y, - **millipede_params) - - - #rename - tray.Add('Rename', - Keys=['SRTSplitInIcePulses_IC_Singles', f'SRTSplitInIcePulses_IC_Singles_{sfx}', - 'PreferredFit_key', f'PreferredFit_key_{sfx}', - 'PreferredFit', f"PreferredFit_{sfx}"]) - #LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW + **millipede_params, + ) + + # rename + tray.Add( + "Rename", + Keys=[ + "SRTSplitInIcePulses_IC_Singles", + f"SRTSplitInIcePulses_IC_Singles_{sfx}", + "PreferredFit_key", + f"PreferredFit_key_{sfx}", + "PreferredFit", + f"PreferredFit_{sfx}", + ], + ) + # LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW def notify1(frame): logger.debug(f"reco complete! {datetime.datetime.now()}") @@ -455,7 +549,8 @@ def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariati posvar_id=frame[cfg.I3FRAME_POSVAR].value, position=frame["Dummy_pos"], time=frame["Dummy_time"].value, - energy=frame["Dummy_time"].value) + energy=frame["Dummy_time"].value, + ) # Provide a standard alias for the reconstruction class provided by this module. From 56bd5d7f35d4981e31e42fcd888c1a8766a83187 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 8 Apr 2026 19:14:35 -0400 Subject: [PATCH 42/63] revert to version d7ff45d --- skymap_scanner/recos/monopod_taupede.py | 33 +++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 4a06455a2..1938927d0 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,31 +1,35 @@ -from common.calculator import poisson_llh import datetime -import random -import time -from typing import List, Final +import os +from typing import Final, List -from icecube.icetray import I3Units # type: ignore[import] -from icecube import ( # type: ignore[import] # noqa: F401 +import numpy +from icecube import ( # noqa: F401 + VHESelfVeto, dataclasses, frame_object_diff, gulliver, gulliver_modules, icetray, + lilliput, millipede, photonics_service, recclasses, simclasses, ) -from icecube.icetray import I3Frame # type: ignore[import] -from skymap_scanner import config as cfg -from skymap_scanner.utils.pixel_classes import RecoPixelVariation -from skymap_scanner.recos import RecoInterface, VertexGenerator -from skymap_scanner.recos.common.pulse_proc import mask_deepcore, pulse_cleaning +from icecube.icetray import I3Frame, I3Units, I3Tray -# IMPORTS FROM REC_TAU -import os +from .. import config as cfg +from ..utils.pixel_classes import RecoPixelVariation +from . import RecoInterface, VertexGenerator +from .common.pulse_proc import mask_deepcore, pulse_cleaning + + +import random + + +# IMPORTS FROM REC_TAU: import argparse from importlib.metadata import version @@ -33,8 +37,6 @@ import numpy as np from icecube import dataio -from icecube import icetray, dataclasses, photonics_service, mue # noqa: F401 -from icecube.icetray import I3Tray, I3Units from icecube.phys_services.which_split import which_split from icecube.millipede import HighEnergyExclusions from icecube.spline_reco import SplineMPE @@ -49,7 +51,6 @@ ) # for gulliver -from icecube import lilliput from icecube.gulliver_modules import gulliview from snowflake import library, unfold From 43d87f2361edf34c5c8dbcbdb340054576d46027 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 9 Apr 2026 12:01:31 -0400 Subject: [PATCH 43/63] added mypy ignore messages from millipede_wilks to monopod_taupede --- skymap_scanner/recos/monopod_taupede.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 1938927d0..829284111 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,3 +1,6 @@ +# fmt: off +# pylint: skip-file +# mypy: ignore-errors import datetime import os from typing import Final, List From ff5483e47df8d4cb7f6c809c2f1d26d0f2635ae0 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 9 Apr 2026 13:09:08 -0400 Subject: [PATCH 44/63] deleted redundant imports --- skymap_scanner/recos/monopod_taupede.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 829284111..9c11ad713 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -65,7 +65,6 @@ maskunhits, maskstrings, maskdust, - pulse_cleaning, ) from reco.truth import truth, druth from reco.mlpd import ( @@ -427,7 +426,6 @@ def notify0(frame): # print( prefs ) # print("running HESE, with printing modules") - from segments.MillipedeWrapper import MillipedeWrapper # energy definition gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" From 1e4184d5b147fb160d625c3b6a5876bca4eaf0a3 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 9 Apr 2026 13:16:22 -0400 Subject: [PATCH 45/63] deleted unecessary imports --- skymap_scanner/recos/monopod_taupede.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 9c11ad713..df25c800d 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -33,7 +33,6 @@ # IMPORTS FROM REC_TAU: -import argparse from importlib.metadata import version from pprint import pformat @@ -41,7 +40,6 @@ from icecube import dataio from icecube.phys_services.which_split import which_split -from icecube.millipede import HighEnergyExclusions from icecube.spline_reco import SplineMPE from icecube.level3_filter_cascade.level3_Recos import SPEFit @@ -74,7 +72,6 @@ preferred, define_splines, ) -from reco.seed import default_seeds class MonoTau(RecoInterface): From 8964ffd93fa7826b562ceebdb34684dff85e7738 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 9 Apr 2026 13:32:14 -0400 Subject: [PATCH 46/63] added missing self param --- skymap_scanner/recos/monopod_taupede.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index df25c800d..995cf73da 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -33,14 +33,12 @@ # IMPORTS FROM REC_TAU: -from importlib.metadata import version from pprint import pformat import numpy as np from icecube import dataio from icecube.phys_services.which_split import which_split -from icecube.spline_reco import SplineMPE from icecube.level3_filter_cascade.level3_Recos import SPEFit # for level 3 muon (pulse cleaning needed for splinempe) @@ -133,7 +131,7 @@ def setup_reco(self): @staticmethod @icetray.traysegment - def prepare_frames(tray, name, logger, **kwargs) -> None: + def prepare_frames(self,tray, name, logger, **kwargs) -> None: # CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED # Generates the vertex seed for the initial scan. # Only run if HESE_VHESelfVeto is not present in the frame. From 39574936787978fde90cd436f29f512dc4f568a1 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 9 Apr 2026 13:37:24 -0400 Subject: [PATCH 47/63] undid 2 deletions to test --- skymap_scanner/recos/monopod_taupede.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 995cf73da..5a5aac87d 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,6 +1,13 @@ # fmt: off # pylint: skip-file # mypy: ignore-errors + + + +from importlib.metadata import version +from icecube.spline_reco import SplineMPE + + import datetime import os from typing import Final, List @@ -131,7 +138,7 @@ def setup_reco(self): @staticmethod @icetray.traysegment - def prepare_frames(self,tray, name, logger, **kwargs) -> None: + def prepare_frames(self, tray, name, logger, **kwargs) -> None: # CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED # Generates the vertex seed for the initial scan. # Only run if HESE_VHESelfVeto is not present in the frame. From 40f6938a9e64ff1d77425b0de2b14117759c8fcd Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 9 Apr 2026 17:27:28 -0400 Subject: [PATCH 48/63] pushing to see if local list of flake8 errors is complete --- skymap_scanner/recos/monopod_taupede.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 5a5aac87d..3e7cdabbc 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -1,10 +1,6 @@ -# fmt: off -# pylint: skip-file -# mypy: ignore-errors -from importlib.metadata import version from icecube.spline_reco import SplineMPE @@ -51,17 +47,10 @@ # for level 3 muon (pulse cleaning needed for splinempe) from icecube import level3_filter_muon # noqa: F401 -# for srt cleaning -from icecube.STTools.seededRT.configuration_services import ( - I3DOMLinkSeededRTConfigurationService, -) # for gulliver from icecube.gulliver_modules import gulliview -from snowflake import library, unfold -import reco -from reco import skymap, dom from reco.masks import ( earlypulses, maskdc, @@ -379,7 +368,7 @@ def notify0(frame): "Pulses": f"{pulses_for_reco}PulseCleaned", "CascadePhotonicsService": self.cascade_service, "MuonPhotonicsService": None, - "ExcludedDOMs": self.excludedDOMs, + "ExcludedDOMs": ExcludedDOMs, "ReadoutWindow": f"{pulses_for_reco}PulseCleanedTimeRange", "PartialExclusion": True, "PhotonsPerBin": 0, @@ -396,7 +385,7 @@ def notify0(frame): for mini in minis: tray.Add( wrapperfn, - f"{mini}_{PPB0}", + f"{mini}_{sfx}", Seed=Seed, Minimizer=mini, Unfold=False, @@ -405,7 +394,7 @@ def notify0(frame): **millipede_params, ) seeder = lilliput.segments.add_seed_service( - tray, millipede_params["Pulses"], [f"{specifier}_{mini}_{PPB0}"] + tray, millipede_params["Pulses"], [f"{specifier}_{mini}_{sfx}"] ) minispec = mini.lower() relerr = 0.05 @@ -414,7 +403,7 @@ def notify0(frame): prefs = [ _ for tup in [ - [f"TaupedeFit_{mini}_{PPB0}", f"MonopodFit_{mini}_{PPB0}"] + [f"TaupedeFit_{mini}_{sfx}", f"MonopodFit_{mini}_{sfx}"] for mini in minis ] for _ in tup From 3c4a1de548261ecc42368154d6a0d61abb7c98d5 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Fri, 10 Apr 2026 10:19:12 -0400 Subject: [PATCH 49/63] pushing changes before switching to test branch --- skymap_scanner/recos/monopod_taupede.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py index 3e7cdabbc..ee0e46c38 100644 --- a/skymap_scanner/recos/monopod_taupede.py +++ b/skymap_scanner/recos/monopod_taupede.py @@ -85,7 +85,6 @@ class MonoTau(RecoInterface): FTP_TMOD_SPLINE, ] - """Logic for a dummy reco.""" def __init__(self, realtime_format_version: str): super().__init__(realtime_format_version) @@ -93,6 +92,11 @@ def __init__(self, realtime_format_version: str): self.refine_time = True # VALUE TAKEN FROM MILLIPEDE_WILKS: self.add_fallback_position = True + + #section taken from millipede_wilks: + self.pulsesName_input = self.get_input_pulses(realtime_format_version) + self.pulsesName = self.pulsesName_input + "IC" + self.pulsesName_cleaned = self.pulsesName+'LatePulseCleaned' def get_vertex_variations() -> List[dataclasses.I3Position]: """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variation.""" From 38805dd957edd6a62786129d7a18bd0ce0e52c63 Mon Sep 17 00:00:00 2001 From: ric-evans Date: Thu, 16 Apr 2026 14:22:03 -0500 Subject: [PATCH 50/63] add `github_token` for `test-run-nsides-thresholds-dummy` job --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 873a5bbed..a596d106e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -421,6 +421,8 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files + secrets: | + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 12 # on average max~=8.5min From 8ef6e805bfd35f1e45568736758732894ee8acc6 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 18 Jun 2026 13:38:51 -0400 Subject: [PATCH 51/63] add current taupede reco attempt --- skymap_scanner/recos/monopodtaupede.py | 604 +++++++++++++++++++++++++ 1 file changed, 604 insertions(+) create mode 100755 skymap_scanner/recos/monopodtaupede.py diff --git a/skymap_scanner/recos/monopodtaupede.py b/skymap_scanner/recos/monopodtaupede.py new file mode 100755 index 000000000..6e5e9c4f8 --- /dev/null +++ b/skymap_scanner/recos/monopodtaupede.py @@ -0,0 +1,604 @@ +"""IceTray segment for a millipede reco.""" + +# fmt: off +# pylint: skip-file +# mypy: ignore-errors + + + +from reco.seed import convert_seeds + + +import datetime +import os +from typing import Final, List, Tuple + +import numpy +from icecube import ( # noqa: F401 + VHESelfVeto, + dataclasses, + frame_object_diff, + gulliver, + gulliver_modules, + icetray, + lilliput, + millipede, + photonics_service, + recclasses, + simclasses, + mue, +) + +from skymap_scanner import config as cfg +from skymap_scanner.utils.pixel_classes import RecoPixelVariation +from skymap_scanner.recos import RecoInterface, VertexGenerator +from skymap_scanner.recos.common.pulse_proc import mask_deepcore, pulse_cleaning + + +import numpy as np + +from icecube.icetray import I3Units, I3Frame, logging +from icecube.millipede import HighEnergyExclusions + +from reco.mlpd import preferred +from reco.seed import convert_to_tau_seed, kmeans +from functools import partial +from icecube.millipede import ( + MonopodFit, + TaupedeFit, +) + +from icecube.phys_services import I3ScaleCalculator + +class MonopodTaupede(RecoInterface): + """Reco logic for monopod+taupede.""" + + # Spline requirements ############################################## + FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" + FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" + FTP_EFFD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.eff.fits" + FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" + FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" + + SPLINE_REQUIREMENTS = [FTP_ABS_SPLINE, FTP_PROB_SPLINE, FTP_EFFD_SPLINE, + FTP_EFFP_SPLINE, FTP_TMOD_SPLINE] + + def __init__(self, realtime_format_version: str): + super().__init__(realtime_format_version) + self.rotate_vertex = True + self.refine_time = True + self.add_fallback_position = True + + self.pulsesName_input = self.get_input_pulses(realtime_format_version) + self.pulsesName = self.pulsesName_input + "IC" + self.pulsesName_cleaned = self.pulsesName+'LatePulseCleaned' + + + @staticmethod + def get_vertex_variations() -> List[dataclasses.I3Position]: + """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variations. + """ + return VertexGenerator.point() + + def setup_reco(self): + datastager = self.get_datastager() + + datastager.stage_files(self.SPLINE_REQUIREMENTS) + + abs_spline: str = datastager.get_filepath(self.FTP_ABS_SPLINE) + prob_spline: str = datastager.get_filepath(self.FTP_PROB_SPLINE) + effd_spline: str = datastager.get_filepath(self.FTP_EFFD_SPLINE) + effp_spline: str = datastager.get_filepath(self.FTP_EFFP_SPLINE) + tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) + + self.cascade_service = photonics_service.I3PhotoSplineService( + abs_spline, prob_spline, timingSigma=0.0, + effectivedistancetable = effd_spline, + tiltTableDir = os.path.expandvars('$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/'), + quantileEpsilon=1, + effectivedistancetableprob = effp_spline, + effectivedistancetabletmod = tmod_spline + ) + + self.muon_service = None + + @icetray.traysegment + def prepare_frames(self, tray, name, logger): + # Generates the vertex seed for the initial scan. + # Only run if HESE_VHESelfVeto is not present in the frame. + # VertexThreshold is 250 in the original HESE analysis (Tianlu) + # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. + def extract_seed(frame): + seed_prefix = "HESE_VHESelfVeto" + frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] + frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] + + tray.Add(extract_seed, "ExtractSeed", + If = lambda frame: frame.Has("HESE_VHESelfVeto")) + + tray.AddModule('VHESelfVeto', 'selfveto', + VertexThreshold=250, + Pulses=self.pulsesName_input+'HLC', + OutputBool='HESE_VHESelfVeto', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: "HESE_VHESelfVeto" not in frame) + + # this only runs if the previous module did not return anything + tray.AddModule('VHESelfVeto', 'selfveto-emergency-lowen-settings', + VertexThreshold=5, + Pulses=self.pulsesName_input+'HLC', + OutputBool='VHESelfVeto_meaningless_lowen', + OutputVertexTime=cfg.INPUT_TIME_NAME, + OutputVertexPos=cfg.INPUT_POS_NAME, + If=lambda frame: not frame.Has("HESE_VHESelfVeto")) + + tray.Add(mask_deepcore, origpulses=self.pulsesName_input, maskedpulses=self.pulsesName) + + @staticmethod + def makeSurePulsesExist(frame, pulsesName) -> None: + if pulsesName not in frame: + raise RuntimeError(f"{pulsesName} not in frame") + if pulsesName + "TimeWindows" not in frame: + raise RuntimeError(f"{pulsesName + 'TimeWindows'} not in frame") + if pulsesName + "TimeRange" not in frame: + raise RuntimeError(f"{pulsesName + 'TimeRange'} not in frame") + + @icetray.traysegment + def exclusions(self, tray, name): + tray.Add('Delete', keys=['BrightDOMs', + 'SaturatedDOMs', + 'DeepCoreDOMs', + self.pulsesName_cleaned, + self.pulsesName_cleaned+'TimeWindows', + self.pulsesName_cleaned+'TimeRange']) + + + exclusionList = \ + tray.AddSegment(HighEnergyExclusions, 'millipede_DOM_exclusions', + Pulses = self.pulsesName, + ExcludeDeepCore='DeepCoreDOMs', + ExcludeSaturatedDOMs='SaturatedDOMs', + ExcludeBrightDOMs='BrightDOMs', + BrightDOMThreshold=2, + BadDomsList='BadDomsList', + CalibrationErrata='CalibrationErrata', + SaturationWindows='SaturationWindows' + ) + + + # I like having frame objects in there even if they are empty for some frames + def createEmptyDOMLists(frame, ListNames=[]): + for name in ListNames: + if name in frame: + continue + frame[name] = dataclasses.I3VectorOMKey() + tray.AddModule(createEmptyDOMLists, 'createEmptyDOMLists', + ListNames = ["BrightDOMs"]) + # exclude bright DOMs + ExcludedDOMs = exclusionList + + def skipunhits(frame, output, pulses): + keepstrings = [1,3,5,14,16,18,20,31,33,35,37,39,51,53,55,57,59,68,70,72,74] + keepoms = list(range(1,60,5)) + all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame( + frame, pulses) + omgeo = frame['I3Geometry'] + geo = omgeo.omgeo + unhits = dataclasses.I3VectorOMKey() + for k, v in geo.items(): + if v.omtype != dataclasses.I3OMGeo.OMType.IceCube: + continue + if k.string not in keepstrings: + if k not in all_pulses.keys(): + unhits.append(k) + else: + if k not in all_pulses.keys() and k.om not in keepoms: + unhits.append(k) + + frame[output] = unhits + + ################## + tray.AddModule(pulse_cleaning, "LatePulseCleaning", + input_pulses_name=self.pulsesName, + output_pulses_name=self.pulsesName_cleaned, + residual=1.5e3*I3Units.ns) + ExcludedDOMs.append(self.pulsesName_cleaned+'TimeWindows') + + tray.Add(skipunhits, output='OtherUnhits', pulses=self.pulsesName_cleaned) + ExcludedDOMs.append('OtherUnhits') + return ExcludedDOMs + + @icetray.traysegment + def MonopodWrapper(self,tray, name, Seed, Iterations=4, Chain=1, **params): + if Chain == 1: + # the amplitude monopod fit + tray.Add( + MonopodFit, + f'seed_MonopodFit_{name}_Amp', + Seed=Seed, + Iterations=Iterations, + PhotonsPerBin=-1, + StepD=60, + StepT=100, + StepZenith=0, + StepAzimuth=0, + **{k: v for k, v in params.items() if k not in ['PhotonsPerBin', 'BinSigma']}, + ) + Seed = [ + f'seed_MonopodFit_{name}_Amp', + ] + + # the multiple iteration timed monopod fit + tray.Add( + MonopodFit, + f'MonopodFit_{name}', + Seed=Seed, + Iterations=Iterations, + StepD=20, + StepT=50, + StepZenith=0, + StepAzimuth=0, + **params, + ) + + return f'MonopodFit_{name}' + + @icetray.traysegment + def TaupedeWrapper(self, tray, name, Seed, Iterations=1, Chain=1, **params): + + def length_penalty(_p): + return max(0., np.log10(abs(_p.length) / I3Units.m)) + + if Chain: + monopod0 = f'MonopodFit_{name}' + tray.Add(self.MonopodWrapper, name, Seed=Seed, Chain=Chain, **params) + + _tparams = { + k: v + for k, v in params.items() + if k + not in [ + 'Minimizer', + ] + } + + _nclusters = 2 + tray.Add( + kmeans, + nclusters=_nclusters, + minit='++', + pulse_type=params['Pulses'], + output_particles_key='KMeansParticles_pp', + split=True, + If=lambda _fr: not _fr.Has('KMeansParticles_pp'), + ) + tray.Add( + kmeans, + nclusters=_nclusters, + minit='points', + pulse_type=params['Pulses'], + output_particles_key='KMeansParticles_points', + split=True, + If=lambda _fr: not _fr.Has('KMeansParticles_points'), + ) + + seedscans_monopod = [] + seedscans_kmeans2 = [] + seedscans_altnfit = [] + for minit in ['pp', 'points']: + for i in range(_nclusters): + # a fast time fit with the output of kmeans(nclusters=2) + kmeans_tfit = f'KMeansParticles_{name}_{minit}{i:03}_T' + tray.Add( + TaupedeFit, + kmeans_tfit, + Seed=f'KMeansParticles_{minit}{i:03}', + StepL=0., + StepT=60., + StepD=0., + StepZenith=0., + StepAzimuth=0., + LengthBounds=[0., 1000.], + **params, + ) + # scan vertex along direction + for dist in range(-100, 101, 20): + kmeans_seed_key = f'seed_{kmeans_tfit}{dist:04}' + tray.Add( + convert_to_tau_seed, + inkey=kmeans_tfit, + outkey=kmeans_seed_key, + length=None, backprop=dist * I3Units.m + ) + seedscans_kmeans2.append(kmeans_seed_key) + + def contained_p2(frame, seed_key): + if frame.Stop != I3Frame.Physics: + return False + + if not frame.Has(seed_key): + return False + + # guarantee seed for short lengths + if frame[seed_key].length < 30 * I3Units.m: + return True + + p2 = frame[seed_key].pos + frame[seed_key].dir * frame[seed_key].length + # this places the second cascade at the monopod vtx, which we trust + # treat the start as the secondary vertex + if '_Backlen' in seed_key: + p2 = frame[seed_key].pos + + # skip if secondary vertex is outside + if abs(p2.z) > 530. * I3Units.m: + return False + + if p2.x**2 + p2.y**2 > (600. * I3Units.m)**2: + return False + + return True + for len in np.unique(np.logspace(0, 2.8, 50, dtype=int)): + monopod_seed_key = f'seed_{name}_Monopod{len:03}' + tray.Add(convert_to_tau_seed, + inkey=monopod0, + outkey=monopod_seed_key, + length=len * I3Units.m) + seedscans_monopod.append(monopod_seed_key) + + backprp_seed_key = f'seed_{name}_Backprp{len:03}' + tray.Add( + convert_to_tau_seed, + inkey=monopod0, + outkey=backprp_seed_key, + length=len * I3Units.m, + backprop=2. * I3Units.m + ) + seedscans_monopod.append(backprp_seed_key) + + if len > 2. * I3Units.m: + backprp_seed_key = f'seed_{name}_Backlen{len:03}' + tray.Add( + convert_to_tau_seed, + inkey=monopod0, + outkey=backprp_seed_key, + length=len * I3Units.m, + backprop=len * I3Units.m, + ) + seedscans_monopod.append(backprp_seed_key) + + for altdir in ['SPEFit2', 'LineFit']: + altdir_seed_key = f'seed_{name}_{altdir}{len:03}' + tray.Add( + convert_to_tau_seed, + inkey=monopod0, + outkey=altdir_seed_key, + length=len * I3Units.m, + dirkey=altdir, + If=lambda _fr, _altdir=altdir: _fr.Has(_altdir), + ) + seedscans_altnfit.append(altdir_seed_key) + + for seed in seedscans_monopod + seedscans_altnfit + seedscans_kmeans2: + tray.Add('TauMillipede', Tau=seed, Output=f'{seed}_Tau', **_tparams, + If=partial(contained_p2, seed_key=seed)) + + tray.Add( + preferred, + i3_particles_fitparams=[(_, f'{_}_TauFitParams') for _ in seedscans_monopod], + output=f'seed_{name}_BestMonopod', + penalty=length_penalty + ) + tray.Add( + preferred, + i3_particles_fitparams=[(_, f'{_}_TauFitParams') for _ in seedscans_kmeans2], + output=f'seed_{name}_BestKMeans2', + penalty=length_penalty + ) + tray.Add( + preferred, + i3_particles_fitparams=[(_, f'{_}_TauFitParams') for _ in seedscans_altnfit], + output=f'seed_{name}_BestAltnFit', + penalty=length_penalty + ) + Seed = [f'seed_{name}_BestMonopod', f'seed_{name}_BestAltnFit', f'seed_{name}_BestKMeans2'] + + if isinstance(Seed, str): + Seed = [Seed,] + + taupede_fits = [] + _outkey = '' + for _seed in Seed: + _outkey = f'{_seed}_TaupedeFit_{name}' + tray.Add( + TaupedeFit, + _outkey, + Seed=_seed, + StepL=20, + StepT=20, + StepD=20, + StepZenith=0, + StepAzimuth=0, + LengthBounds=[0, 1000], + Iterations=Iterations, + **params, + ) + taupede_fits.append(_outkey) + + tray.Add( + preferred, + i3_particles_fitparams=[(_, f'{_}FitParams') for _ in taupede_fits], + output=f'TaupedeFit_{name}', + penalty=length_penalty + ) + def copy_params_particles(frame): + print(name) + pref_key = frame[f'TaupedeFit_{name}_key'].value + + if frame.Has(f'{pref_key}FitParams'): + frame[f'TaupedeFit_{name}FitParams'] = frame[f'{pref_key}FitParams'] + else: + logging.log_warn( + f'Frame does not contain "{pref_key}FitParams", check if I3SimpleFitter was successful' + ) + + if frame.Has(f'{pref_key}Particles'): + frame[f'TaupedeFit_{name}Particles'] = frame[f'{pref_key}Particles'] + else: + logging.log_warn( + f'Frame does not contain "{pref_key}Particles", check if I3SimpleFitter was successful' + ) + + tray.Add(copy_params_particles) + + return _outkey + + + @icetray.traysegment + def traysegment(self, tray, name, logger, seed=None): + """Perform MonopodTaupede reco.""" + #take out prep frames after testing + #tray.AddSegment(self.prepare_frames,"prepareframes",logger=logger) + ExcludedDOMs = tray.Add(self.exclusions) + + tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) + + def check_cal(frame): + cal = frame['I3Calibration'] + logger.debug('Mean SPEs') + for omkey in list(cal.dom_cal.keys())[::100]: + x = cal.dom_cal[omkey] + mean_spe = dataclasses.mean_spe_charge(x) + logger.debug(f'...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}') + logger.debug(f'......: {x.combined_spe_charge_distribution.compensation_factor}') + tray.Add(check_cal) + + def notify0(frame): + logger.debug(f"starting a new fit ({name})! {datetime.datetime.now()}") + tray.AddModule(notify0, "notify0") + + + seed= tray.AddSegment(convert_seeds, name, hypo='cascade', seeds=seed) + + reco_params = { + 'Pulses': self.pulsesName_cleaned, + 'CascadePhotonicsService': self.cascade_service, + 'MuonPhotonicsService': self.muon_service, + 'ExcludedDOMs': ExcludedDOMs, + 'PartialExclusion': True, + 'ShowerRegularization': 1.e-12, + 'MinTimeWidth':12, + 'BinSigma': np.nan, + 'RelUncertainty':0.05, + 'ReadoutWindow':self.pulsesName_cleaned + 'TimeRange', + 'Minimizer':'iMIGRAD', + 'UseUnhitDOMS': True, + 'PhotonsPerBin':0, + } + + + + tray.Add(self.TaupedeWrapper, + 'Final', + Seed=seed, + Iterations=1, + Chain=1, + **reco_params) + + + + def UpdateStepXYZ(the_steps, direction, uniform_step=15*I3Units.m): + the_steps['StepX'] = numpy.sqrt(1-direction.x**2)*uniform_step + the_steps['StepY'] = numpy.sqrt(1-direction.y**2)*uniform_step + the_steps['StepZ'] = numpy.sqrt(1-direction.z**2)*uniform_step + + @classmethod + def to_recopixelvariation(cls, frame: I3Frame, geometry: I3Frame) -> RecoPixelVariation: + # Calculate reco losses, based on load_scan_state() + reco_losses_inside, reco_losses_total = cls.get_reco_losses_inside( + p_frame=frame, g_frame=geometry, + ) + + if 'TaupedeFit_FinalFitParams' not in frame: + llh = float("nan") + else: + llh = frame['TaupedeFit_FinalFitParams'].logl + return RecoPixelVariation( + nside=frame[cfg.I3FRAME_NSIDE].value, + pixel_id=frame[cfg.I3FRAME_PIXEL].value, + llh=llh, + reco_losses_inside=reco_losses_inside, + reco_losses_total=reco_losses_total, + posvar_id=frame[cfg.I3FRAME_POSVAR].value, + position=frame['TaupedeFit_FinalParticles'][0].pos, + time=frame['TaupedeFit_FinalParticles'][0].time, + energy=frame['TaupedeFit_FinalParticles'][0].energy, + ) + + @staticmethod + def get_reco_losses_inside(p_frame: I3Frame, g_frame: I3Frame) -> Tuple[float, float]: + + if 'TaupedeFit_FinalParticles' not in p_frame: + return numpy.nan, numpy.nan + recoParticle = p_frame['TaupedeFit_FinalParticles'] + + calc = I3ScaleCalculator(g_frame["I3Geometry"], I3ScaleCalculator.IC86_STRICT, I3ScaleCalculator.IT81_STRICT) + + """ + def getRecoLosses(vecParticles): + losses = [] + for p in vecParticles: + if not p.is_cascade: + continue + if p.energy == 0.: + continue + losses.append([p.time, p.energy]) + return losses + recoLosses = getRecoLosses(p_frame['TaupedeFit_FinalParticles']) + + intersectionPoints = VHESelfVeto.IntersectionsWithInstrumentedVolume(g_frame["I3Geometry"], recoParticle) + intersectionTimes = [] + for intersectionPoint in intersectionPoints: + vecX = intersectionPoint.x - recoParticle.pos.x + vecY = intersectionPoint.y - recoParticle.pos.y + vecZ = intersectionPoint.z - recoParticle.pos.z + + prod = vecX*recoParticle.dir.x + vecY*recoParticle.dir.y + vecZ*recoParticle.dir.z + dist = numpy.sqrt(vecX**2 + vecY**2 + vecZ**2) + if prod < 0.: + dist *= -1. + intersectionTimes.append(dist/dataclasses.I3Constants.c + recoParticle.time) + + entryTime = None + exitTime = None + intersectionTimes = sorted(intersectionTimes) + if len(intersectionTimes) == 0: + return 0., 0. + + entryTime = intersectionTimes[0]-60.*I3Units.m/dataclasses.I3Constants.c + intersectionTimes = intersectionTimes[1:] + exitTime = intersectionTimes[-1]+60.*I3Units.m/dataclasses.I3Constants.c + intersectionTimes = intersectionTimes[:-1] + + totalRecoLosses = 0. + totalRecoLossesInside = 0. + for entry in recoLosses: + totalRecoLosses += entry[1] + if entryTime is not None and entry[0] < entryTime: + continue + if exitTime is not None and entry[0] > exitTime: + continue + totalRecoLossesInside += entry[1] + """ + + totalRecoLosses = 0. + totalRecoLossesInside = 0. + for particle in recoParticle: + totalRecoLosses += particle.energy + contained = calc.vertex_is_inside(particle) + if contained: + totalRecoLossesInside += particle.energy + + return totalRecoLossesInside, totalRecoLosses + +RECO_CLASS: Final[type[RecoInterface]] = MonopodTaupede From 7a1450af8426330aff541b2533beccc8d45f8d95 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 18 Jun 2026 13:54:18 -0400 Subject: [PATCH 52/63] decrease complexity of reco wrappers used --- skymap_scanner/recos/monopodtaupede.py | 138 ++++++++++++------------- 1 file changed, 68 insertions(+), 70 deletions(-) diff --git a/skymap_scanner/recos/monopodtaupede.py b/skymap_scanner/recos/monopodtaupede.py index 6e5e9c4f8..8045bbe49 100755 --- a/skymap_scanner/recos/monopodtaupede.py +++ b/skymap_scanner/recos/monopodtaupede.py @@ -211,19 +211,18 @@ def skipunhits(frame, output, pulses): @icetray.traysegment def MonopodWrapper(self,tray, name, Seed, Iterations=4, Chain=1, **params): - if Chain == 1: - # the amplitude monopod fit - tray.Add( - MonopodFit, - f'seed_MonopodFit_{name}_Amp', - Seed=Seed, - Iterations=Iterations, - PhotonsPerBin=-1, - StepD=60, - StepT=100, - StepZenith=0, - StepAzimuth=0, - **{k: v for k, v in params.items() if k not in ['PhotonsPerBin', 'BinSigma']}, + # the amplitude monopod fit + tray.Add( + MonopodFit, + f'seed_MonopodFit_{name}_Amp', + Seed=Seed, + Iterations=Iterations, + PhotonsPerBin=-1, + StepD=60, + StepT=100, + StepZenith=0, + StepAzimuth=0, + **{k: v for k, v in params.items() if k not in ['PhotonsPerBin', 'BinSigma']}, ) Seed = [ f'seed_MonopodFit_{name}_Amp', @@ -250,68 +249,67 @@ def TaupedeWrapper(self, tray, name, Seed, Iterations=1, Chain=1, **params): def length_penalty(_p): return max(0., np.log10(abs(_p.length) / I3Units.m)) - if Chain: - monopod0 = f'MonopodFit_{name}' - tray.Add(self.MonopodWrapper, name, Seed=Seed, Chain=Chain, **params) + monopod0 = f'MonopodFit_{name}' + tray.Add(self.MonopodWrapper, name, Seed=Seed, Chain=Chain, **params) - _tparams = { - k: v - for k, v in params.items() - if k - not in [ - 'Minimizer', - ] - } + _tparams = { + k: v + for k, v in params.items() + if k + not in [ + 'Minimizer', + ] + } - _nclusters = 2 - tray.Add( - kmeans, - nclusters=_nclusters, - minit='++', - pulse_type=params['Pulses'], - output_particles_key='KMeansParticles_pp', - split=True, - If=lambda _fr: not _fr.Has('KMeansParticles_pp'), - ) - tray.Add( - kmeans, - nclusters=_nclusters, - minit='points', - pulse_type=params['Pulses'], - output_particles_key='KMeansParticles_points', - split=True, - If=lambda _fr: not _fr.Has('KMeansParticles_points'), - ) + _nclusters = 2 + tray.Add( + kmeans, + nclusters=_nclusters, + minit='++', + pulse_type=params['Pulses'], + output_particles_key='KMeansParticles_pp', + split=True, + If=lambda _fr: not _fr.Has('KMeansParticles_pp'), + ) + tray.Add( + kmeans, + nclusters=_nclusters, + minit='points', + pulse_type=params['Pulses'], + output_particles_key='KMeansParticles_points', + split=True, + If=lambda _fr: not _fr.Has('KMeansParticles_points'), + ) - seedscans_monopod = [] - seedscans_kmeans2 = [] - seedscans_altnfit = [] - for minit in ['pp', 'points']: - for i in range(_nclusters): - # a fast time fit with the output of kmeans(nclusters=2) - kmeans_tfit = f'KMeansParticles_{name}_{minit}{i:03}_T' + seedscans_monopod = [] + seedscans_kmeans2 = [] + seedscans_altnfit = [] + for minit in ['pp', 'points']: + for i in range(_nclusters): + # a fast time fit with the output of kmeans(nclusters=2) + kmeans_tfit = f'KMeansParticles_{name}_{minit}{i:03}_T' + tray.Add( + TaupedeFit, + kmeans_tfit, + Seed=f'KMeansParticles_{minit}{i:03}', + StepL=0., + StepT=60., + StepD=0., + StepZenith=0., + StepAzimuth=0., + LengthBounds=[0., 1000.], + **params, + ) + # scan vertex along direction + for dist in range(-100, 101, 20): + kmeans_seed_key = f'seed_{kmeans_tfit}{dist:04}' tray.Add( - TaupedeFit, - kmeans_tfit, - Seed=f'KMeansParticles_{minit}{i:03}', - StepL=0., - StepT=60., - StepD=0., - StepZenith=0., - StepAzimuth=0., - LengthBounds=[0., 1000.], - **params, + convert_to_tau_seed, + inkey=kmeans_tfit, + outkey=kmeans_seed_key, + length=None, backprop=dist * I3Units.m ) - # scan vertex along direction - for dist in range(-100, 101, 20): - kmeans_seed_key = f'seed_{kmeans_tfit}{dist:04}' - tray.Add( - convert_to_tau_seed, - inkey=kmeans_tfit, - outkey=kmeans_seed_key, - length=None, backprop=dist * I3Units.m - ) - seedscans_kmeans2.append(kmeans_seed_key) + seedscans_kmeans2.append(kmeans_seed_key) def contained_p2(frame, seed_key): if frame.Stop != I3Frame.Physics: From 3c1fc97c37693dcc964aea0973ebf99d9089f034 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 18 Jun 2026 14:35:33 -0400 Subject: [PATCH 53/63] remove old reco class attempt --- skymap_scanner/recos/monopod_taupede.py | 554 ------------------------ 1 file changed, 554 deletions(-) delete mode 100644 skymap_scanner/recos/monopod_taupede.py diff --git a/skymap_scanner/recos/monopod_taupede.py b/skymap_scanner/recos/monopod_taupede.py deleted file mode 100644 index ee0e46c38..000000000 --- a/skymap_scanner/recos/monopod_taupede.py +++ /dev/null @@ -1,554 +0,0 @@ - - - -from icecube.spline_reco import SplineMPE - - -import datetime -import os -from typing import Final, List - -import numpy -from icecube import ( # noqa: F401 - VHESelfVeto, - dataclasses, - frame_object_diff, - gulliver, - gulliver_modules, - icetray, - lilliput, - millipede, - photonics_service, - recclasses, - simclasses, -) - - -from icecube.icetray import I3Frame, I3Units, I3Tray - -from .. import config as cfg -from ..utils.pixel_classes import RecoPixelVariation -from . import RecoInterface, VertexGenerator -from .common.pulse_proc import mask_deepcore, pulse_cleaning - - -import random - - -# IMPORTS FROM REC_TAU: - -from pprint import pformat -import numpy as np - -from icecube import dataio -from icecube.phys_services.which_split import which_split -from icecube.level3_filter_cascade.level3_Recos import SPEFit - -# for level 3 muon (pulse cleaning needed for splinempe) -from icecube import level3_filter_muon # noqa: F401 - - -# for gulliver -from icecube.gulliver_modules import gulliview - -from reco.masks import ( - earlypulses, - maskdc, - maskunhits, - maskstrings, - maskdust, -) -from reco.truth import truth, druth -from reco.mlpd import ( - MonopodWrapper, - TaupedeWrapper, - MillipedeWrapper, - preferred, - define_splines, -) - - -class MonoTau(RecoInterface): - # SPLINE SETTINGS TAKEN FROM MILLIPEDE_WILKS: - - FTP_ABS_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.abs.fits" - FTP_PROB_SPLINE = "cascade_single_spice_ftp-v1_flat_z20_a5.prob.v2.fits" - FTP_EFFD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.eff.fits" - FTP_EFFP_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.prob.fits" - FTP_TMOD_SPLINE = "cascade_effectivedistance_spice_ftp-v1_z20.tmod.fits" - - SPLINE_REQUIREMENTS = [ - FTP_ABS_SPLINE, - FTP_PROB_SPLINE, - FTP_EFFD_SPLINE, - FTP_EFFP_SPLINE, - FTP_TMOD_SPLINE, - ] - - - def __init__(self, realtime_format_version: str): - super().__init__(realtime_format_version) - self.rotate_vertex = True - self.refine_time = True - # VALUE TAKEN FROM MILLIPEDE_WILKS: - self.add_fallback_position = True - - #section taken from millipede_wilks: - self.pulsesName_input = self.get_input_pulses(realtime_format_version) - self.pulsesName = self.pulsesName_input + "IC" - self.pulsesName_cleaned = self.pulsesName+'LatePulseCleaned' - - def get_vertex_variations() -> List[dataclasses.I3Position]: - """Returns a list of vectors referenced to the origin that will be used to generate the vertex position variation.""" - return VertexGenerator.point() - - def setup_reco(self): - # SECTION TAKEN FROM MILLIPEDE_WILKS: - datastager = self.get_datastager() - - datastager.stage_files(self.SPLINE_REQUIREMENTS) - - abs_spline: str = datastager.get_filepath(self.FTP_ABS_SPLINE) - prob_spline: str = datastager.get_filepath(self.FTP_PROB_SPLINE) - effd_spline: str = datastager.get_filepath(self.FTP_EFFD_SPLINE) - effp_spline: str = datastager.get_filepath(self.FTP_EFFP_SPLINE) - tmod_spline: str = datastager.get_filepath(self.FTP_TMOD_SPLINE) - - self.cascade_service = photonics_service.I3PhotoSplineService( - abs_spline, - prob_spline, - timingSigma=0.0, - effectivedistancetable=effd_spline, - tiltTableDir=os.path.expandvars( - "$I3_BUILD/ice-models/resources/models/ICEMODEL/spice_ftp-v1/" - ), - quantileEpsilon=1, - effectivedistancetableprob=effp_spline, - effectivedistancetabletmod=tmod_spline, - ) - - self.muon_service = None - - @staticmethod - @icetray.traysegment - def prepare_frames(self, tray, name, logger, **kwargs) -> None: - # CURRENTLY USING THE VHESELFVETO FROM MILLIPEDE WILKS FOR CONSISTENCY, CAN CHANGE THIS IF NEEDED - # Generates the vertex seed for the initial scan. - # Only run if HESE_VHESelfVeto is not present in the frame. - # VertexThreshold is 250 in the original HESE analysis (Tianlu) - # If HESE_VHESelfVeto is already in the frame, is likely using implicitly a VertexThreshold of 250 already. To be determined when this is not the case. - def extract_seed(frame): - seed_prefix = "HESE_VHESelfVeto" - frame[cfg.INPUT_POS_NAME] = frame[seed_prefix + "VertexPos"] - frame[cfg.INPUT_TIME_NAME] = frame[seed_prefix + "VertexTime"] - - tray.Add( - extract_seed, "ExtractSeed", If=lambda frame: frame.Has("HESE_VHESelfVeto") - ) - - tray.AddModule( - "VHESelfVeto", - "selfveto", - VertexThreshold=250, - Pulses=self.pulsesName_input + "HLC", - OutputBool="HESE_VHESelfVeto", - OutputVertexTime=cfg.INPUT_TIME_NAME, - OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: "HESE_VHESelfVeto" not in frame, - ) - - # this only runs if the previous module did not return anything - tray.AddModule( - "VHESelfVeto", - "selfveto-emergency-lowen-settings", - VertexThreshold=5, - Pulses=self.pulsesName_input + "HLC", - OutputBool="VHESelfVeto_meaningless_lowen", - OutputVertexTime=cfg.INPUT_TIME_NAME, - OutputVertexPos=cfg.INPUT_POS_NAME, - If=lambda frame: not frame.Has("HESE_VHESelfVeto"), - ) - - tray.Add( - mask_deepcore, - origpulses=self.pulsesName_input, - maskedpulses=self.pulsesName, - ) - - # OTHER METHODS FROM MILLIPEDE_WILKS: - def makeSurePulsesExist(frame, pulsesName) -> None: - if pulsesName not in frame: - raise RuntimeError(f"{pulsesName} not in frame") - if pulsesName + "TimeWindows" not in frame: - raise RuntimeError(f"{pulsesName + 'TimeWindows'} not in frame") - if pulsesName + "TimeRange" not in frame: - raise RuntimeError(f"{pulsesName + 'TimeRange'} not in frame") - - @icetray.traysegment - def exclusions(self, tray, name): - tray.Add( - "Delete", - keys=[ - "BrightDOMs", - "SaturatedDOMs", - "DeepCoreDOMs", - self.pulsesName_cleaned, - self.pulsesName_cleaned + "TimeWindows", - self.pulsesName_cleaned + "TimeRange", - ], - ) - - exclusionList = tray.AddSegment( - millipede.HighEnergyExclusions, - "millipede_DOM_exclusions", - Pulses=self.pulsesName, - ExcludeDeepCore="DeepCoreDOMs", - ExcludeSaturatedDOMs="SaturatedDOMs", - ExcludeBrightDOMs="BrightDOMs", - BrightDOMThreshold=2, - BadDomsList="BadDomsList", - CalibrationErrata="CalibrationErrata", - SaturationWindows="SaturationWindows", - ) - - # I like having frame objects in there even if they are empty for some frames - def createEmptyDOMLists(frame, ListNames=[]): - for name in ListNames: - if name in frame: - continue - frame[name] = dataclasses.I3VectorOMKey() - - tray.AddModule( - createEmptyDOMLists, "createEmptyDOMLists", ListNames=["BrightDOMs"] - ) - # exclude bright DOMs - ExcludedDOMs = exclusionList - - def skipunhits(frame, output, pulses): - keepstrings = [ - 1, - 3, - 5, - 14, - 16, - 18, - 20, - 31, - 33, - 35, - 37, - 39, - 51, - 53, - 55, - 57, - 59, - 68, - 70, - 72, - 74, - ] - keepoms = list(range(1, 60, 5)) - all_pulses = dataclasses.I3RecoPulseSeriesMap.from_frame(frame, pulses) - omgeo = frame["I3Geometry"] - geo = omgeo.omgeo - unhits = dataclasses.I3VectorOMKey() - for k, v in geo.items(): - if v.omtype != dataclasses.I3OMGeo.OMType.IceCube: - continue - if k.string not in keepstrings: - if k not in all_pulses.keys(): - unhits.append(k) - else: - if k not in all_pulses.keys() and k.om not in keepoms: - unhits.append(k) - - frame[output] = unhits - - ################## - tray.AddModule( - pulse_cleaning, - "LatePulseCleaning", - input_pulses_name=self.pulsesName, - output_pulses_name=self.pulsesName_cleaned, - residual=1.5e3 * I3Units.ns, - ) - ExcludedDOMs.append(self.pulsesName_cleaned + "TimeWindows") - - tray.Add(skipunhits, output="OtherUnhits", pulses=self.pulsesName_cleaned) - ExcludedDOMs.append("OtherUnhits") - return ExcludedDOMs - - # SEPARATE METHODS FROM REC_TAU.PY: - def sane(frame, split_names): - for split_name in split_names: - if which_split(split_name=split_name)(frame): - return True - return False - - """def print_frameid(frame): - eventid = frame['I3EventHeader'].event_id - print("*******Currently processing frame %s*******" %eventid)""" - - def fixed_dir(filelist, isdata, hypo, split_names, nframes=None): - truths = [] - - def extract(frame): - truths.append(frame["cc"].dir) - - tray = I3Tray() - tray.Add("I3Reader", Filenamelist=filelist) - tray.Add(sane, split_names=split_names) - if isdata: - tray.Add(druth, hypo=hypo) - else: - tray.Add(truth, hypo=hypo) - tray.Add(extract) - if nframes is None: - tray.Execute() - else: - tray.Execute(nframes) - if len(set([(_.zenith, _.azimuth) for _ in truths])) != 1: - icetray.logging.log_warn( - "The number of extracted, unique true dirs is not 1, not updating stepXYZ" - ) - return None - return truths[0] - - @staticmethod - # TRAYSEGMENT MODIFIED FROM MAIN OF REC_TAU.PY - @icetray.traysegment - # USING MILLIPEDE WILKS FORMAT FOR ARGUMENTS OF THE FUNCTION - def traysegment(self, tray, name, logger, seed): - - # TAKEN FROM MILLIPEDE_WILKS: - ExcludedDOMs = tray.Add(self.exclusions) - - tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) - - def check_cal(frame): - cal = frame["I3Calibration"] - logger.debug("Mean SPEs") - for omkey in list(cal.dom_cal.keys())[::100]: - x = cal.dom_cal[omkey] - mean_spe = dataclasses.mean_spe_charge(x) - logger.debug(f"...{omkey}: {mean_spe} {x.mean_atwd_charge_correction}") - logger.debug( - f"......: {x.combined_spe_charge_distribution.compensation_factor}" - ) - - tray.Add(check_cal) - - def notify0(frame): - logger.debug(f"starting a new fit ({name})! {datetime.datetime.now()}") - - tray.AddModule(notify0, "notify0") - - # BEGIN REC_TAU - wrapperfn = TaupedeWrapper - specifier = "TaupedeFit" - loss_vector_suffix = "Particles" - # STARTING WITH THE DEFAULT ITERATIONS NUMBER - iterations = 2 - # TOOK OUT THE TRAY INITIALIZATION AND ADDING I3 READER SO THAT THAT CAN BE DONE SEPARATELY - # USED DEFAULT FOR SPLIT NAMES - tray.Add( - sane, - split_names=[ - "InIceSplit", - ], - ) - # tray.Add(print_frameid) - - # CODE TO RUN WHEN ISDATA=NONE - tray.Add(truth, hypo="tau", If=lambda frame: not frame.Has("cc")) - - # LEAVING OUT SEED CHAIN FOR NOW - - # LEAVING OUT PULSE CLEANING EXCLUDED DOMS SINCE THOSE ARE IN SEPARATE METHODS IN MILLIPEDE_WILKS - # SETTING PULSES FOR RECO TO DEFAULT - pulses_for_reco = "SplitInIcePulses" - millipede_params = { - "Pulses": f"{pulses_for_reco}PulseCleaned", - "CascadePhotonicsService": self.cascade_service, - "MuonPhotonicsService": None, - "ExcludedDOMs": ExcludedDOMs, - "ReadoutWindow": f"{pulses_for_reco}PulseCleanedTimeRange", - "PartialExclusion": True, - "PhotonsPerBin": 0, - "UseUnhitDOMs": not False, - "MinTimeWidth": 16, - "BinSigma": np.nan, - "RelUncertainty": 0.05, - "StepZenith": 0, - "StepAzimuth": 0, - } - icetray.logging.log_info(pformat(millipede_params), __name__) - minis = [_ for _ in ["MIGRAD", "iMIGRAD", "SIMPLEX", "iSIMPLEX", "LBFGSB"]] - sfx = "PPB0" - for mini in minis: - tray.Add( - wrapperfn, - f"{mini}_{sfx}", - Seed=Seed, - Minimizer=mini, - Unfold=False, - Chain=1, - Iterations=iterations, - **millipede_params, - ) - seeder = lilliput.segments.add_seed_service( - tray, millipede_params["Pulses"], [f"{specifier}_{mini}_{sfx}"] - ) - minispec = mini.lower() - relerr = 0.05 - minispec += f".relerr{relerr:.2f}" - - prefs = [ - _ - for tup in [ - [f"TaupedeFit_{mini}_{sfx}", f"MonopodFit_{mini}_{sfx}"] - for mini in minis - ] - for _ in tup - ] - tray.Add( - preferred, - i3_particles_fitparams=[(_, f"{_}FitParams") for _ in prefs], - If=lambda f: len(prefs) > 0 and any([f.Has(_) for _ in prefs]), - ) - - # print( prefs ) - - # print("running HESE, with printing modules") - - # energy definition - gcdfilepath = "/cvmfs/icecube.opensciencegrid.org/data/GCD/GeoCalibDetectorStatus_2020.Run134142.Pass2_V0.i3.gz" - gcdfile = dataio.I3File(gcdfilepath) - frame = gcdfile.pop_frame() - - while "I3Geometry" not in frame: - frame = gcdfile.pop_frame() - geometry = frame["I3Geometry"].omgeo - - strings = [ - 1, - 2, - 3, - 4, - 5, - 6, - 13, - 21, - 30, - 40, - 50, - 59, - 67, - 74, - 73, - 72, - 78, - 77, - 76, - 75, - 68, - 60, - 51, - 41, - 31, - 22, - 14, - 7, - ] - - outerbounds = {} - cx, cy = [], [] - for string in strings: - omkey = icetray.OMKey(string, 1) - # if geometry.has_key(omkey): - x, y = geometry[omkey].position.x, geometry[omkey].position.y - outerbounds[string] = (x, y) - cx.append(x) - cy.append(y) - cx, cy = np.asarray(cx), np.asarray(cy) - order = np.argsort(np.arctan2(cx, cy)) - outeredge_x = cx[order] - outeredge_y = cy[order] - - # print(sfx) - - # SHOULD I TAKE THIS OUT SINCE ITS TRACK - # track reco - tray.Add( - "I3OMSelection", - "omselection_HESE", - InputResponse="SRT" + "SplitInIcePulses", - OmittedStrings=[79, 80, 81, 82, 83, 84, 85, 86], # deepcore strings - OutputOMSelection=f"SRTSplitInIcePulses_BadOMSelectionString_{sfx}", - OutputResponse=f"SRTSplitInIcePulses_IC_Singles_{sfx}", - ) - - tray.Add( - SPEFit, - f"SPEFit16_{sfx}", - Pulses=f"SRTSplitInIcePulses_IC_Singles_{sfx}", - Iterations=16, - ) - - del millipede_params[ - "PhotonsPerBin" - ] # also input to MillipedeWrapper next, gives error if entered twice - - # HESE millipede - tray.Add( - MillipedeWrapper, - f"HESEMillipedeFit_{sfx}", - seed_cascade=f"MonopodFit_iMIGRAD_{sfx}", - seed_tau=f"TaupedeFit_iMIGRAD_{sfx}", - seed_track=f"SPEFit16_{sfx}", - PhotonsPerBin=0, - ShowerSpacing=5, - innerboundary=550, - outerboundary=650, - outeredge_x=outeredge_x, - outeredge_y=outeredge_y, - **millipede_params, - ) - - # rename - tray.Add( - "Rename", - Keys=[ - "SRTSplitInIcePulses_IC_Singles", - f"SRTSplitInIcePulses_IC_Singles_{sfx}", - "PreferredFit_key", - f"PreferredFit_key_{sfx}", - "PreferredFit", - f"PreferredFit_{sfx}", - ], - ) - # LEAVING FINAL ORPHAN STREAM DROPPING AND FILE SAVING FOR WHEN YOU CALL THE FUNCTION FOR NOW - - def notify1(frame): - logger.debug(f"reco complete! {datetime.datetime.now()}") - - tray.AddModule(notify1, "notify1") - - @staticmethod - def to_recopixelvariation(frame: I3Frame, geometry: I3Frame) -> RecoPixelVariation: - return RecoPixelVariation( - nside=frame[cfg.I3FRAME_NSIDE].value, - pixel_id=frame[cfg.I3FRAME_PIXEL].value, - llh=frame["Dummy_llh"].value, - reco_losses_inside=random.random(), - reco_losses_total=random.random(), - posvar_id=frame[cfg.I3FRAME_POSVAR].value, - position=frame["Dummy_pos"], - time=frame["Dummy_time"].value, - energy=frame["Dummy_time"].value, - ) - - -# Provide a standard alias for the reconstruction class provided by this module. -RECO_CLASS: Final[type[RecoInterface]] = MonoTau From 415d3b5c90bcf6aae672bb8a63d635d4acd6652e Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Thu, 18 Jun 2026 15:54:30 -0400 Subject: [PATCH 54/63] fix linter errors --- skymap_scanner/recos/monopodtaupede.py | 256 +++++++++++++------------ 1 file changed, 129 insertions(+), 127 deletions(-) diff --git a/skymap_scanner/recos/monopodtaupede.py b/skymap_scanner/recos/monopodtaupede.py index 8045bbe49..7ec736304 100755 --- a/skymap_scanner/recos/monopodtaupede.py +++ b/skymap_scanner/recos/monopodtaupede.py @@ -211,18 +211,19 @@ def skipunhits(frame, output, pulses): @icetray.traysegment def MonopodWrapper(self,tray, name, Seed, Iterations=4, Chain=1, **params): - # the amplitude monopod fit - tray.Add( - MonopodFit, - f'seed_MonopodFit_{name}_Amp', - Seed=Seed, - Iterations=Iterations, - PhotonsPerBin=-1, - StepD=60, - StepT=100, - StepZenith=0, - StepAzimuth=0, - **{k: v for k, v in params.items() if k not in ['PhotonsPerBin', 'BinSigma']}, + if Chain == 1: + # the amplitude monopod fit + tray.Add( + MonopodFit, + f'seed_MonopodFit_{name}_Amp', + Seed=Seed, + Iterations=Iterations, + PhotonsPerBin=-1, + StepD=60, + StepT=100, + StepZenith=0, + StepAzimuth=0, + **{k: v for k, v in params.items() if k not in ['PhotonsPerBin', 'BinSigma']}, ) Seed = [ f'seed_MonopodFit_{name}_Amp', @@ -242,6 +243,32 @@ def MonopodWrapper(self,tray, name, Seed, Iterations=4, Chain=1, **params): ) return f'MonopodFit_{name}' + + def contained_p2(self,frame, seed_key): + if frame.Stop != I3Frame.Physics: + return False + + if not frame.Has(seed_key): + return False + + # guarantee seed for short lengths + if frame[seed_key].length < 30 * I3Units.m: + return True + + p2 = frame[seed_key].pos + frame[seed_key].dir * frame[seed_key].length + # this places the second cascade at the monopod vtx, which we trust + # treat the start as the secondary vertex + if '_Backlen' in seed_key: + p2 = frame[seed_key].pos + + # skip if secondary vertex is outside + if abs(p2.z) > 530. * I3Units.m: + return False + + if p2.x**2 + p2.y**2 > (600. * I3Units.m)**2: + return False + + return True @icetray.traysegment def TaupedeWrapper(self, tray, name, Seed, Iterations=1, Chain=1, **params): @@ -249,137 +276,113 @@ def TaupedeWrapper(self, tray, name, Seed, Iterations=1, Chain=1, **params): def length_penalty(_p): return max(0., np.log10(abs(_p.length) / I3Units.m)) - monopod0 = f'MonopodFit_{name}' - tray.Add(self.MonopodWrapper, name, Seed=Seed, Chain=Chain, **params) + if Chain: + monopod0 = f'MonopodFit_{name}' + tray.Add(self.MonopodWrapper, name, Seed=Seed, Chain=Chain, **params) - _tparams = { - k: v - for k, v in params.items() - if k - not in [ - 'Minimizer', - ] - } + _tparams = { + k: v + for k, v in params.items() + if k + not in [ + 'Minimizer', + ] + } - _nclusters = 2 - tray.Add( - kmeans, - nclusters=_nclusters, - minit='++', - pulse_type=params['Pulses'], - output_particles_key='KMeansParticles_pp', - split=True, - If=lambda _fr: not _fr.Has('KMeansParticles_pp'), - ) - tray.Add( - kmeans, - nclusters=_nclusters, - minit='points', - pulse_type=params['Pulses'], - output_particles_key='KMeansParticles_points', - split=True, - If=lambda _fr: not _fr.Has('KMeansParticles_points'), - ) + _nclusters = 2 + tray.Add( + kmeans, + nclusters=_nclusters, + minit='++', + pulse_type=params['Pulses'], + output_particles_key='KMeansParticles_pp', + split=True, + If=lambda _fr: not _fr.Has('KMeansParticles_pp'), + ) + tray.Add( + kmeans, + nclusters=_nclusters, + minit='points', + pulse_type=params['Pulses'], + output_particles_key='KMeansParticles_points', + split=True, + If=lambda _fr: not _fr.Has('KMeansParticles_points'), + ) - seedscans_monopod = [] - seedscans_kmeans2 = [] - seedscans_altnfit = [] - for minit in ['pp', 'points']: - for i in range(_nclusters): - # a fast time fit with the output of kmeans(nclusters=2) - kmeans_tfit = f'KMeansParticles_{name}_{minit}{i:03}_T' - tray.Add( - TaupedeFit, - kmeans_tfit, - Seed=f'KMeansParticles_{minit}{i:03}', - StepL=0., - StepT=60., - StepD=0., - StepZenith=0., - StepAzimuth=0., - LengthBounds=[0., 1000.], - **params, - ) - # scan vertex along direction - for dist in range(-100, 101, 20): - kmeans_seed_key = f'seed_{kmeans_tfit}{dist:04}' + seedscans_monopod = [] + seedscans_kmeans2 = [] + seedscans_altnfit = [] + for minit in ['pp', 'points']: + for i in range(_nclusters): + # a fast time fit with the output of kmeans(nclusters=2) + kmeans_tfit = f'KMeansParticles_{name}_{minit}{i:03}_T' tray.Add( - convert_to_tau_seed, - inkey=kmeans_tfit, - outkey=kmeans_seed_key, - length=None, backprop=dist * I3Units.m + TaupedeFit, + kmeans_tfit, + Seed=f'KMeansParticles_{minit}{i:03}', + StepL=0., + StepT=60., + StepD=0., + StepZenith=0., + StepAzimuth=0., + LengthBounds=[0., 1000.], + **params, ) - seedscans_kmeans2.append(kmeans_seed_key) - - def contained_p2(frame, seed_key): - if frame.Stop != I3Frame.Physics: - return False - - if not frame.Has(seed_key): - return False - - # guarantee seed for short lengths - if frame[seed_key].length < 30 * I3Units.m: - return True - - p2 = frame[seed_key].pos + frame[seed_key].dir * frame[seed_key].length - # this places the second cascade at the monopod vtx, which we trust - # treat the start as the secondary vertex - if '_Backlen' in seed_key: - p2 = frame[seed_key].pos - - # skip if secondary vertex is outside - if abs(p2.z) > 530. * I3Units.m: - return False + # scan vertex along direction + for dist in range(-100, 101, 20): + kmeans_seed_key = f'seed_{kmeans_tfit}{dist:04}' + tray.Add( + convert_to_tau_seed, + inkey=kmeans_tfit, + outkey=kmeans_seed_key, + length=None, backprop=dist * I3Units.m + ) + seedscans_kmeans2.append(kmeans_seed_key) + + for len in np.unique(np.logspace(0, 2.8, 50, dtype=int)): + monopod_seed_key = f'seed_{name}_Monopod{len:03}' + tray.Add(convert_to_tau_seed, + inkey=monopod0, + outkey=monopod_seed_key, + length=len * I3Units.m) + seedscans_monopod.append(monopod_seed_key) - if p2.x**2 + p2.y**2 > (600. * I3Units.m)**2: - return False + backprp_seed_key = f'seed_{name}_Backprp{len:03}' + tray.Add( + convert_to_tau_seed, + inkey=monopod0, + outkey=backprp_seed_key, + length=len * I3Units.m, + backprop=2. * I3Units.m + ) + seedscans_monopod.append(backprp_seed_key) - return True - for len in np.unique(np.logspace(0, 2.8, 50, dtype=int)): - monopod_seed_key = f'seed_{name}_Monopod{len:03}' - tray.Add(convert_to_tau_seed, - inkey=monopod0, - outkey=monopod_seed_key, - length=len * I3Units.m) - seedscans_monopod.append(monopod_seed_key) - - backprp_seed_key = f'seed_{name}_Backprp{len:03}' + if len > 2. * I3Units.m: + backprp_seed_key = f'seed_{name}_Backlen{len:03}' tray.Add( convert_to_tau_seed, inkey=monopod0, outkey=backprp_seed_key, length=len * I3Units.m, - backprop=2. * I3Units.m + backprop=len * I3Units.m, ) seedscans_monopod.append(backprp_seed_key) - if len > 2. * I3Units.m: - backprp_seed_key = f'seed_{name}_Backlen{len:03}' - tray.Add( - convert_to_tau_seed, - inkey=monopod0, - outkey=backprp_seed_key, - length=len * I3Units.m, - backprop=len * I3Units.m, - ) - seedscans_monopod.append(backprp_seed_key) - - for altdir in ['SPEFit2', 'LineFit']: - altdir_seed_key = f'seed_{name}_{altdir}{len:03}' - tray.Add( - convert_to_tau_seed, - inkey=monopod0, - outkey=altdir_seed_key, - length=len * I3Units.m, - dirkey=altdir, - If=lambda _fr, _altdir=altdir: _fr.Has(_altdir), - ) - seedscans_altnfit.append(altdir_seed_key) + for altdir in ['HESE_SPEFit2', 'HESE_MuonImprovedLineFit']: + altdir_seed_key = f'seed_{name}_{altdir}{len:03}' + tray.Add( + convert_to_tau_seed, + inkey=monopod0, + outkey=altdir_seed_key, + length=len * I3Units.m, + dirkey=altdir, + If=lambda _fr, _altdir=altdir: _fr.Has(_altdir), + ) + seedscans_altnfit.append(altdir_seed_key) for seed in seedscans_monopod + seedscans_altnfit + seedscans_kmeans2: tray.Add('TauMillipede', Tau=seed, Output=f'{seed}_Tau', **_tparams, - If=partial(contained_p2, seed_key=seed)) + If=partial(self.contained_p2, seed_key=seed)) tray.Add( preferred, @@ -430,7 +433,6 @@ def contained_p2(frame, seed_key): penalty=length_penalty ) def copy_params_particles(frame): - print(name) pref_key = frame[f'TaupedeFit_{name}_key'].value if frame.Has(f'{pref_key}FitParams'): @@ -456,7 +458,7 @@ def copy_params_particles(frame): def traysegment(self, tray, name, logger, seed=None): """Perform MonopodTaupede reco.""" #take out prep frames after testing - #tray.AddSegment(self.prepare_frames,"prepareframes",logger=logger) + tray.AddSegment(self.prepare_frames,"prepareframes",logger=logger) ExcludedDOMs = tray.Add(self.exclusions) tray.Add(self.makeSurePulsesExist, pulsesName=self.pulsesName_cleaned) From 7f7f92dd3f03e02facf5eac3909831a959be3243 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 24 Jun 2026 09:02:57 -0400 Subject: [PATCH 55/63] put build secrets back into the publish.yml --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f06425972..3e8c8dc0f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: image-publish: - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.25 + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@docker-build-secret permissions: # for GITHUB_TOKEN packages: write with: From c5f4ca379b161a667e251e0fd33d18a2953fd95e Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 24 Jun 2026 09:09:27 -0400 Subject: [PATCH 56/63] undo previous change --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3e8c8dc0f..f06425972 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,7 +14,7 @@ on: jobs: image-publish: - uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@docker-build-secret + uses: WIPACrepo/wipac-dev-workflows/.github/workflows/image-publish.yml@v1.25 permissions: # for GITHUB_TOKEN packages: write with: From cd2821b4afd1943181e1025814ab34c03e0f23a9 Mon Sep 17 00:00:00 2001 From: Emma Tintinger - UMD Grad Student Date: Wed, 24 Jun 2026 11:45:58 -0400 Subject: [PATCH 57/63] rewrite secrets lines --- .github/workflows/tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a596d106e..dd3c9c350 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -216,7 +216,7 @@ jobs: tags: skymap_scanner:py-dep-this load: true secrets: | - github_token=$${{ secrets.PERSONAL_ACCESS_TOKEN }} + GIT_AUTH_TOKEN=$${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -242,7 +242,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: false # for checking if this builds, no loading needed secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + GITHUB_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} test-run-dummy: needs: [ flake8 ] @@ -281,7 +281,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} @@ -422,7 +422,7 @@ jobs: load: true build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 12 # on average max~=8.5min @@ -519,7 +519,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 55 # on average max~=35min @@ -620,7 +620,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run run: | @@ -675,7 +675,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} + GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 10 # on average max~=5min From 9079c1b62c02f4b23cf0c0cf68555d0fff093877 Mon Sep 17 00:00:00 2001 From: nega Date: Wed, 24 Jun 2026 12:41:39 -0400 Subject: [PATCH 58/63] align Dockerfile secret mount w/ workflow --- .github/workflows/tests.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dd3c9c350..a596d106e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -216,7 +216,7 @@ jobs: tags: skymap_scanner:py-dep-this load: true secrets: | - GIT_AUTH_TOKEN=$${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=$${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 @@ -242,7 +242,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: false # for checking if this builds, no loading needed secrets: | - GITHUB_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} test-run-dummy: needs: [ flake8 ] @@ -281,7 +281,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files - if: ${{ matrix.container_platform == 'apptainer' }} @@ -422,7 +422,7 @@ jobs: load: true build-args: INCLUDE_GCD=0 # for dummy tests we don't need GCD files secrets: | - GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 12 # on average max~=8.5min @@ -519,7 +519,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 55 # on average max~=35min @@ -620,7 +620,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run run: | @@ -675,7 +675,7 @@ jobs: tags: ${{ env._SCANNER_IMAGE_DOCKER }} load: true secrets: | - GIT_AUTH_TOKEN=${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: run timeout-minutes: 10 # on average max~=5min From 824b12e70f892b69e6f906a434926e87f50e9dd4 Mon Sep 17 00:00:00 2001 From: nega Date: Wed, 24 Jun 2026 13:37:27 -0400 Subject: [PATCH 59/63] test fgPAT --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a596d106e..8e5497c2a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -216,7 +216,7 @@ jobs: tags: skymap_scanner:py-dep-this load: true secrets: | - github_token=$${{ secrets.PERSONAL_ACCESS_TOKEN }} + github_token=$${{ secrets.nega_test_tok }} build-args: INCLUDE_GCD=0 # for pip recording we don't need GCD files - if: ${{ steps.pydep-precheck.outputs.do_generation == 'true' }} uses: WIPACrepo/wipac-dev-py-dependencies-action@v3.4 From 3010919119d9a908065fd5ee4e8e5259757367db Mon Sep 17 00:00:00 2001 From: nega Date: Wed, 24 Jun 2026 13:47:16 -0400 Subject: [PATCH 60/63] shamefully catting the test token to the world --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 62a6288f7..7c281b383 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,6 +36,7 @@ RUN --mount=type=bind,source=.,target=/src,rw \ pip install /src[rabbitmq] #INSTALL RECO REPOSITORY +RUN --mount=type=secret,id=github_token echo "ENTERNING THE DUMBZONE\n\n" && cat /run/secrets/github_token && echo "\n\n" RUN --mount=type=secret,id=github_token \ git clone \ https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ From b8e2515eb1ff2935af88dfaceab2897cb1fc2625 Mon Sep 17 00:00:00 2001 From: nega Date: Wed, 24 Jun 2026 13:52:38 -0400 Subject: [PATCH 61/63] remove token from url --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7c281b383..28195b5bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,10 +36,9 @@ RUN --mount=type=bind,source=.,target=/src,rw \ pip install /src[rabbitmq] #INSTALL RECO REPOSITORY -RUN --mount=type=secret,id=github_token echo "ENTERNING THE DUMBZONE\n\n" && cat /run/secrets/github_token && echo "\n\n" RUN --mount=type=secret,id=github_token \ git clone \ - https://$(cat /run/secrets/github_token)@github.com/icecube/reco.git \ + https://@github.com/icecube/reco.git \ /opt/reco_src RUN --mount=type=cache,target=/tmp/pip-cache \ pip install /opt/reco_src From fda48dbe8c7cfae00e13cefcb60a789f7cf63cbd Mon Sep 17 00:00:00 2001 From: nega Date: Wed, 24 Jun 2026 14:10:42 -0400 Subject: [PATCH 62/63] switch to env var --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 28195b5bb..2d500da51 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,9 +36,9 @@ RUN --mount=type=bind,source=.,target=/src,rw \ pip install /src[rabbitmq] #INSTALL RECO REPOSITORY -RUN --mount=type=secret,id=github_token \ +RUN --mount=type=secret,id=github_token,env=GITHUB_TOKEN \ git clone \ - https://@github.com/icecube/reco.git \ + https://${GITHUB_TOKEN}@github.com/icecube/reco.git \ /opt/reco_src RUN --mount=type=cache,target=/tmp/pip-cache \ pip install /opt/reco_src From 7b903c70d90d55195fbcc7312e2d6ef4b7f3279b Mon Sep 17 00:00:00 2001 From: nega Date: Wed, 24 Jun 2026 14:23:03 -0400 Subject: [PATCH 63/63] add username --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2d500da51..a404ce566 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,7 @@ RUN --mount=type=bind,source=.,target=/src,rw \ #INSTALL RECO REPOSITORY RUN --mount=type=secret,id=github_token,env=GITHUB_TOKEN \ git clone \ - https://${GITHUB_TOKEN}@github.com/icecube/reco.git \ + https://gha:${GITHUB_TOKEN}@github.com/icecube/reco.git \ /opt/reco_src RUN --mount=type=cache,target=/tmp/pip-cache \ pip install /opt/reco_src