SND-LHC / sndsw

SND@LHC experiment framework based on FairShip and FairRoot
6 stars 17 forks source link

Veto and US time alignment #214

Open siilieva opened 7 months ago

siilieva commented 7 months ago

Here we can discuss the implementation of the US and the Veto time alignment.

For this first implementation, when one needs the time calibration for a single channel, they have to use the detector method GetCorrectedTime as before. However, for multi-channel readout (Veto, US, DS horizontal), if an operation is to be performed using all SiPM timings per hit, apply_t_corr flag and SipmDistance parameters are added to all timing getters in the MuFilterHit class. This way the user can call one function per hit that could perform the time calibration for all channels before returning the time value - impact time, delta L/R time. It is not optimal that there are now 2 ways to return the calibration time - through the detector ( per signle channel at a time!) and through the hit (for multi-channel hits).

olantwin commented 7 months ago

First comment: love the long commit messages! Thanks for taking the time for adding the information!

olantwin commented 7 months ago

The apply_t_corr name is good for the flag. Not too long, but very easy to understand.

Still thinking about a better name for OptForTimeCorrections, not convinced by that name either. Maybe just PrepareTimes? CopyOrCorrectTimes? Especially since the default behaviour is to not correct, we should not have a name that might be misread as applying corrections.

Maybe it would be better to move the conditional into the calling function and then just have a helper function to apply the correction (i.e. the else case of OptForTimeCorrections)?

siilieva commented 7 months ago

PrepareTimes with extension 'PrepareTimesHelper' would match exactly what this function does.

In the current implementation, I use OptForTimeCorrections() to set fTimesHelper[16], which is either a copy of the raw times or the newly calculated corrected times. It seems an elegant way that checks for the apply_t_corr flag once per hit(saves time on ifs). If one moves the conditional into the Getters, they'll have to call the time correction function channel by channel and repeat a block of if-do in each Getter. But then also, the fTimesHelper array will become redundant, which is an advantage. I didn't check explicitly what is the computational time/mem. load for each of these two approaches in terms of time. That will decide best. I'll do and provide the estimates.

siilieva commented 7 months ago

The table below summarizes the results if one calls the OptForTimeCorrections once per hit (currently approach on the branch) or the proposed realization calling a function that would apply the correction per individual channel only if apply_t_corr is True. In terms of time the first approach (labelled A in the tables) performs better. In terms of allocated memory, the second approach (B in the tables) performs slightly better as expected since one doesn't need the helper array fTimesHelper. I'd go with the original approach since the computing time is less, especially in the case the time correction is required (~10% faster per event). On the mem side, the difference is small anyways. Screenshot from 2024-04-03 17-27-02

I'm attaching the code+ used to test these two cases, but the main difference is back-end of the MuFilterHit class and is not visible there. The values in the table are best out of 5 trials per case.

def process_memory(): process = psutil.Process(os.getpid()) mem_info = process.memory_info() return mem_info.rss

sndgeo = SndlhcGeo.GeoInterface('/eos/experiment/sndlhc/convertedData/physics/2023/geofile_sndlhc_TI18_V5_2023.root') mufi = ROOT.gROOT.GetListOfGlobals().FindObject("MuFilter")

ch = ROOT.TChain("rawConv") ch.Add("/eos/experiment/sndlhc/convertedData/physics/2022/run_004541/sndsw_raw-0020.root") ch.GetEntry(0) mufi.InitEvent(ch.EventHeader)

N = 1000000 start = time.perf_counter() mem_before = process_memory() for i, event in enumerate(ch): if i==N-1: break for hit in event.Digi_MuFilterHits: if hit.GetSystem()!=3: hit.GetAllTimes(True, False, 0) mem_after = process_memory() stop = time.perf_counter()

print("Seconds per call {}".format((stop - start)/N)) print("consumed total memory:",mem_before, mem_after, mem_after - mem_before)

olantwin commented 7 months ago

Is the difference in memory or time even significant?

Unless you want to perform higher stats tests, I would propose that we go with the simpler solution.

olantwin commented 7 months ago

Tip: Markdown supports table like so:

test column a b
1 2 3 4
r c c l
siilieva commented 7 months ago

The difference in time will become significant for analyses that use large number of events with numerous hits(shower events). The quoted times are 'time per event' and are averaged over 1M events. Then 9% faster per event for millions of events is a lot of time. The memory wont be an issue in any case.

siilieva commented 7 months ago

Tip: Markdown supports table like so: test column a b 1 2 3 4 r c c l

I didn't expect that the image of the table would be so ugly pasted here... I had the table already done and wanted to save time.

olantwin commented 7 months ago

The difference in time will become significant for analyses that use large number of events with numerous hits(shower events). The quoted times are 'time per event' and are averaged over 1M events. Then 9% faster per event for millions of events is a lot of time. The memory wont be an issue in any case.

Completely agree that this can quickly add up.

My question was whether the time-difference per event is significant. Execution time can fluctuate a lot depending on the load, data, cache and many other factors. 1M events 5 times might not be enough to establish which is faster (it would be good to know the spread between the 5 runs---at least to have a rough estimate of the uncertainty).

olantwin commented 7 months ago

Tip: Markdown supports table like so: test column a b 1 2 3 4 r c c l

I didn't expect that the image of the table would be so ugly pasted here... I had the table already done and wanted to save time.

No worries, for next time. And your table is elaborate enough, that it would be a pain to do in markdown.

siilieva commented 7 months ago

The difference in time will become significant for analyses that use large number of events with numerous hits(shower events). The quoted times are 'time per event' and are averaged over 1M events. Then 9% faster per event for millions of events is a lot of time. The memory wont be an issue in any case.

Completely agree that this can quickly add up.

My question was whether the time-difference per event is significant. Execution time can fluctuate a lot depending on the load, data, cache and many other factors. 1M events 5 times might not be enough to establish which is faster (it would be good to know the spread between the 5 runs---at least to have a rough estimate of the uncertainty).

The sigma/mean ~ 3% for both realizations when asking for time alignment. I dont have the estimate for the case when no time corrections are asked for. So the 9% speed gain is legit.

olantwin commented 7 months ago

The sigma/mean ~ 3% for both realizations when asking for time alignment. I dont have the estimate for the case when no time corrections are asked for. So the 9% speed gain is legit.

OK, in that case the difference when not applying corrections seems insignificant and the speedup in the case of applying corrections is small, but probably significant. Thanks!

Then we proceed with A?

siilieva commented 7 months ago

Yes, approach A it is. Then on the name OptForTimeCorrections, I'd change it to PrepareTimesHelper to match best what the function does. Meanwhile Andrew is x-checking if the code works as expected.