diff --git a/Project.toml b/Project.toml index 2e0f3155f93ea45128b14386cfbb22b8ef1cb4d6..4c0d2b69af3f55b5139aaf52738bacee76d182dc 100644 --- a/Project.toml +++ b/Project.toml @@ -27,7 +27,7 @@ KM3ioKM3DBExt = "KM3DB" DocStringExtensions = "0.8, 0.9" HDF5 = "^0.16.15, ^0.17" KM3DB = "0.2.2" -KM3NeTTestData = "^0.4.14" +KM3NeTTestData = "^0.4.16" StaticArrays = "1" UnROOT = "^0.10.26" julia = "1" diff --git a/docs/Project.toml b/docs/Project.toml index 5b4aa2f413eb7f646b0a547236e3d84deb9077de..0fbf4f4a27b8124fce0d5f78bd9466b18a777297 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -12,4 +12,4 @@ PGFPlotsX = "8314cec4-20b6-5062-9cdb-752b83310925" [compat] Documenter = "1" FHist = "^0.11" -KM3NeTTestData = "^0.4.15" +KM3NeTTestData = "^0.4.16" diff --git a/docs/src/api.md b/docs/src/api.md index 6054714f9e04b75e1d6a84de468c127eb8df8803..6d85e8718e5ff43e1cc82e16f512c0c17b22433d 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -131,6 +131,7 @@ CHClient ### General tools ```@docs +SummarysliceIntervalIterator getevent categorize nthbitset diff --git a/src/exports.jl b/src/exports.jl index bb7e003967cf3563b98a62ab7666f1549d7ad777..bad67b989490e2209a361e5635418e24cabdb689 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -110,8 +110,12 @@ hasshowerfit, hasshowerpositionfit, hasshowerprefit, -# Utils +# Tools MCEventMatcher, +SummarysliceIntervalIterator, +getevent, + +# Utils categorize, is3dmuon, is3dshower, @@ -120,7 +124,6 @@ isnb, most_frequent, nthbitset, triggered, -getevent, # Physics and math helpers CherenkovPhoton, diff --git a/src/root/online.jl b/src/root/online.jl index aa91483aa3559863a917bb7cd798c507a7e4ea70..e76c58b6a499a86dfb33e0a27c9fcc82c04a4446 100644 --- a/src/root/online.jl +++ b/src/root/online.jl @@ -113,6 +113,9 @@ struct Summaryslice header::SummarysliceHeader frames::Vector{SummaryFrame} end +function Base.show(io::IO, s::Summaryslice) + print(io, "Summaryslice($(length(s.frames)) frames)") +end struct SummarysliceContainer # For performance reasons we use directly the lazy types of UnROOT # We could also parametrise it. diff --git a/src/tools/helpers.jl b/src/tools/helpers.jl index d8149848e54d5d8c0dadc4d950fea7aeb4a25331..1681552d9873ff621bd125630c2306a280a6930c 100644 --- a/src/tools/helpers.jl +++ b/src/tools/helpers.jl @@ -58,3 +58,91 @@ function getevent(tree::T, frame_index, trigger_counter) where T<:Union{OnlineTr end getevent(tree::OfflineTree, idx) = tree[idx] getevent(tree::OnlineTree, idx) = tree.events[idx] + + +""" + +An iterator which yields a `Vector{Summaryslice}` containing summaryslices of a given +`time_interval` (in seconds). Useful when analysing summary data with fixed time intervals. +The returned summaryslices are also sorted in time. + +# Examples +``` +julia> f = ROOTFile("/Volumes/Ultraspeed/Data/Obelix/KM3NeT_00000133_00014728.root") +ROOTFile{OnlineTree (83509 events, 106969 summaryslices)} + +julia> sii = SummarysliceIntervalIterator(f, 60) +SummarysliceIntervalIterator (10753.1s, 60s intervals, 180 chunks) + +julia> for summaryslices in sii + @show length(summaryslices) + @show summaryslices[1].header + break + end +length(summaryslices) = 440 +(summaryslices[1]).header = SummarysliceHeader(133, 14728, 134, UTCExtended(1676246413, 400000000, 0)) +``` + +!!! note + Short time intervals (usually less than a few tens of seconds) will likely return empty + `Vector{Summaryslice}`s in the first few iterations due to a delay in run changes. + Additionally, the number of frames per summaryslice will gradually increase due + to the asynchronous nature of the run transition. See the example below with a time + inteval of 5s. + +```julia-repl +julia> sii = SummarysliceIntervalIterator(f, 5) +SummarysliceIntervalIterator (10753.1s, 5s intervals, 2151 chunks) + +julia> for summaryslices in sii + n = length(summaryslices) + @show n + n > 0 && break + end +n = 0 +n = 0 +n = 5 +``` + +""" +struct SummarysliceIntervalIterator + sc::KM3io.SummarysliceContainer + time_interval::Int # [s] + n_chunks::Int + timespan::Float64 + indices::Vector{Int} + function SummarysliceIntervalIterator(f::ROOTFile, time_interval) + ss = f.online.summaryslices + sorted_summaryslice_indices = sortperm([sh.frame_index for sh in ss.headers]) + timespan = ss.headers[sorted_summaryslice_indices[end]].frame_index / 10 + n_chunks = Int(ceil(timespan / time_interval)) + new(f.online.summaryslices, time_interval, n_chunks, timespan, sorted_summaryslice_indices) + end +end +function Base.show(io::IO, sii::SummarysliceIntervalIterator) + print(io, "SummarysliceIntervalIterator ($(sii.timespan)s, $(sii.time_interval)s intervals, $(sii.n_chunks) chunks)") +end +Base.eltype(::Type{SummarysliceIntervalIterator}) = Vector{KM3io.Summaryslice} +Base.length(sii::SummarysliceIntervalIterator) = sii.n_chunks +function Base.iterate(sii::SummarysliceIntervalIterator, state=(chunk_idx=1, s_idx=1)) + state.chunk_idx > sii.n_chunks && return nothing + + out_size = sii.time_interval * 10 + out = empty!(Vector{KM3io.Summaryslice}(undef, out_size)) + + frame_index_upper = state.chunk_idx * sii.time_interval * 10 + + s_idx = state.s_idx + while s_idx <= length(sii.indices) + idx = sii.indices[s_idx] + summaryslice = sii.sc[idx] + if summaryslice.header.frame_index < frame_index_upper + push!(out, summaryslice) + s_idx += 1 + else + break + end + end + + return (out, (chunk_idx=state.chunk_idx+1, s_idx=s_idx)) +end diff --git a/test/tools.jl b/test/tools.jl index aff37baaa272fc7c4e8a26af91bff14af2f1b398..bb13ed89887d8f2d3edb9969f085422ba52c0384 100644 --- a/test/tools.jl +++ b/test/tools.jl @@ -336,3 +336,25 @@ end @test π/2 ≈ angle(Direction(1.,0,0), Direction(0.,0,1)) @test π ≈ angle(Direction(1.,0,0), Direction(-1.,0,0)) end + + +@testset "SummarysliceIntervalIterator" begin + f = ROOTFile(datapath("online", "summaryslices.root")) + + sii = SummarysliceIntervalIterator(f, 2) + suslis = collect(sii) + @test 55 == length(suslis) + @test 0 == length(suslis[1]) + @test 1 == length(suslis[2]) + @test 2 == length(suslis[3]) + @test all(20 .== length.(suslis[4:end-5])) + @test 8 == length(suslis[end-1]) + @test 6 == length(suslis[end]) + + flat_suslis = vcat(suslis...) + frame_indices = [s.header.frame_index for s in flat_suslis] + @test issorted(frame_indices) + @test frame_indices == sort([s.header.frame_index for s in f.online.summaryslices]) + + close(f) +end