From a2e9ecc734dd5159b2a1f8720c4007c7a0446c47 Mon Sep 17 00:00:00 2001
From: Wojciech Krupa <wojciech.krupa@cern.ch>
Date: Fri, 7 Feb 2025 13:55:45 +0100
Subject: [PATCH] Squashed commit of the following:

commit 9899e93dda1a5d4acdb67e0d12a911ee36e5ab49
Author: Gitlab CI <noreply@cern.ch>
Date:   Fri Feb 7 11:46:53 2025 +0000

    pre-commit fixes

    patch generated by https://gitlab.cern.ch/lhcb/Rec/-/jobs/50602420

commit 5d54670cea06224b36434550e72e1a9d02e90c6c
Author: Wojciech Krupa <wojciech.krupa@cern.ch>
Date:   Fri Feb 7 12:45:37 2025 +0100

    implementation of suggestions from software mainteiners

commit eb795ea0db16c7b97f0cf50cccf8eeeda1543803
Author: Wojciech Krupa <wojciech.krupa@cern.ch>
Date:   Fri Feb 7 12:45:09 2025 +0100

    removing code which goes in different MR

commit 8ad04b2b8e1090866b29b5324faefea4d1e64153
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Feb 5 15:45:56 2025 +0100

    Fixed test_decayfinder for undefined behavior

    This test is using heavily cppyy, but ownership and scoping of C++ object is inexistent there, leading to undefined behavior
    The fix is as horrible as the code : make all local variables global to be sure nothing is deleted which should not be.
    This test should probaby be rewritten in plain C++

commit b8f51801b366f622c0545376abb7732015c4b95e
Author: Chishuai Wang <chishuai.wang@cern.ch>
Date:   Fri Feb 7 09:46:08 2025 +0000

    FT Beam Time Analysis

commit 24d84649229d5f386f2d103a501d15c61c8b6817
Author: RefBot <lhcbsoft@cern.ch>
Date:   Thu Feb 6 18:01:15 2025 +0100

    Update References for: LHCb!4925 based on lhcb-master-mr/12476

    [skip ci]

commit 126442f11a400c0abb917f8151ddf7076274bdca
Author: RefBot <lhcbsoft@cern.ch>
Date:   Wed Feb 5 18:21:28 2025 +0100

    Update References for: Rec!4258 based on lhcb-master-mr/12456

    [skip ci]

commit acaec379c5f2325fd3a57d5118bbef97f9eff7d3
Author: Chris Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Wed Feb 5 11:24:42 2025 +0000

    Update TestFileDB tag for RICH 2023 data tests

commit 9daad83778c09a89edd78e13fd2d57741d48f95e
Author: Miguel Ruiz Diaz <miguel.ruiz.diaz@cern.ch>
Date:   Wed Feb 5 12:15:38 2025 +0000

    Eta distribution of track residuals added to FTTrackMonitor

commit 089f01e686a8ed4238c00ecf25c262fdfd6b70e6
Author: Laurent Dufour <laurent.dufour@cern.ch>
Date:   Wed Feb 5 08:07:00 2025 +0000

    Algorithm to apply a global coordinate transform to PVs

commit 0be4ee2f9f75dafa307ddb8265d8889c778775d6
Author: Marco Clemencic <marco.clemencic@cern.ch>
Date:   Tue Feb 4 11:19:06 2025 +0100

    Add dependencies to be able to collect pytest tests in LoKiPhys

commit fe4ad5b17761aba4c1fbab97b8b418ea9d772e7e
Author: Yingrui Hou <yingrui.hou@cern.ch>
Date:   Tue Feb 4 11:39:05 2025 +0100

    Add a map of the SciFi hit efficiency

commit 71cbbe820b96ae9b7fabac0ce5b42490e5a4a051
Author: Mark Waterlaat <mark.waterlaat@cern.ch>
Date:   Tue Feb 4 10:55:21 2025 +0100

    Add track error to in sensor check

commit 53106fe1352ddabd114218ee67bdb972f345504f
Author: RefBot <lhcbsoft@cern.ch>
Date:   Fri Jan 31 06:00:45 2025 +0100

    Update References for: Detector!667 based on lhcb-master-mr/12409

    [skip ci]

commit cf5496a3f1a35e9501b1d07588251f04f0c94e89
Author: Chris Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Fri Jan 31 11:20:47 2025 +0000

    Adapt RichFutureRecSys/examples/RichFuture.py to new options format

commit 35dfe12cad122bb41d5f6319c440c74d6ccf1640
Author: Michel De Cian <michel.de.cian@cern.ch>
Date:   Fri Jan 31 10:23:33 2025 +0100

    Fix MuonUT truthmatching in PrTrackAssociator

commit 2430664d7e6100a234c97db8beaf620da10e806e
Author: Gitlab CI <noreply@cern.ch>
Date:   Thu Feb 6 14:32:21 2025 +0000

    pre-commit fixes

    patch generated by https://gitlab.cern.ch/lhcb/Rec/-/jobs/50549692

commit 8b48dfe2acf0de481e3d1b6d850614fdf5324f7f
Author: Wojciech Krupa <wojciech.krupa@cern.ch>
Date:   Thu Feb 6 15:31:42 2025 +0100

    implementation of mainteiners suggestions

commit 63818812eae8058b6220e2534a70c633ba578b42
Author: Wojciech Krupa <wojciech.krupa@cern.ch>
Date:   Thu Feb 6 10:55:40 2025 +0100

    fixing default input

commit 80fb43370ef49f0742b51ab3de9a6d57337eb32f
Author: Gitlab CI <noreply@cern.ch>
Date:   Thu Feb 6 09:07:57 2025 +0000

    pre-commit fixes

    patch generated by https://gitlab.cern.ch/lhcb/Rec/-/jobs/50527840

commit 99b45b4786ecffedc1a678f2fe8bed14627320e6
Author: Wojciech Krupa <wojciech.krupa@cern.ch>
Date:   Thu Feb 6 10:07:20 2025 +0100

    cleaning the code in terms of unused parameter warnings

commit e1f217c6cddb98ff8edae7face9160260d55732f
Author: Gitlab CI <noreply@cern.ch>
Date:   Fri Jan 31 13:51:19 2025 +0000

    pre-commit fixes

    patch generated by https://gitlab.cern.ch/lhcb/Rec/-/jobs/50199317

commit c6ccf71ad2fc9e1ddb0588a1549beb23cce16195
Author: Wojciech Krupa <wojciech.krupa@cern.ch>
Date:   Fri Jan 31 14:43:54 2025 +0100

    implementation of new ut monitors

commit 0b3fe2d8c5b3bf1bb1bdd68b54d79e07dba22ab4
Merge: 62950ce792 eb485834e4
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Wed Jan 29 22:39:39 2025 +0100

    Merge branch 'suppress-psabi-note-on-arm-gcc' into 'master'

    Suppress psabi compiler notes on ARM/GCC functor builds

    See merge request lhcb/Rec!4246

commit eb485834e4d0c701415f53065deae36be7affec4
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Wed Jan 29 22:39:39 2025 +0100

    Suppress psabi compiler notes on ARM/GCC functor builds

commit 62950ce79280242de7ff71483665517255fd4360
Merge: 462014aa7b e0ab21256a
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Tue Jan 28 17:59:08 2025 +0100

    Merge branch 'ft-nzs-digit-moni-test-extend-timeout' into 'master'

    Extend timeout for FT nzs_digit_monitoring test to 1200 to cover dbg builds

    See merge request lhcb/Rec!4245

commit e0ab21256a43e88f3e8806a10b8320988d5d15b6
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Tue Jan 28 17:59:08 2025 +0100

    Extend timeout for FT nzs_digit_monitoring test to 1200 to cover dbg builds

commit 462014aa7b57f0c11f988c0d345c27a8bc296275
Merge: f2b6d96adc 27382eff1e
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 28 11:06:47 2025 +0100

    Merge branch 'better-use-of-pytest' into 'master'

    Replace gaudi_add_tests(pytest...) with gaudi_add_pytest(...)

    See merge request lhcb/Rec!4243

commit 27382eff1e86884da44c4bdcc747c3ea4dd1773f
Author: Marco Clemencic <marco.clemencic@cern.ch>
Date:   Mon Jan 27 10:43:03 2025 +0100

    Replace gaudi_add_tests(pytest...) with gaudi_add_pytest(...)

    and adapt some tests to work better under pytest

commit f2b6d96adc5abb5275a37ded68f8e9df1e64c80b
Merge: d0c08479b3 7f9194c1b7
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Fri Jan 24 18:24:33 2025 +0100

    Merge branch 'add-arm-specific-refs' into 'master'

    Add some additional arm specific ref files

    See merge request lhcb/Rec!4240

commit 7f9194c1b76786eb87fe12bed63612643236a86c
Author: Chris Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Fri Jan 24 16:30:31 2025 +0000

    Add some additional arm specific ref files

commit d0c08479b325b9d340a6bfc397bec912c146115a
Merge: 2750265e12 8ea62e659a
Author: Mark Smith <mark.smith@cern.ch>
Date:   Fri Jan 24 11:46:43 2025 +0100

    Merge branch 'release_v37r1' into 'master'

    Release v37r1

    See merge request lhcb/Rec!4239

commit 8ea62e659adf6cb3cd4d8ee39fa48d8214eb7bcf
Author: Mark Smith <mark.smith@cern.ch>
Date:   Fri Jan 24 11:21:48 2025 +0100

    Release v37r1

commit 2750265e12553ea2bcb32578656995b8bf949989
Merge: 56c53d6979 100d1571d2
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Jan 23 14:38:36 2025 +0100

    Merge branch 'use-pre-commit' into 'master'

    Update clang-format and switch to Ruff for Python linting and formatting

    See merge request lhcb/Rec!4224

commit 100d1571d2315cfce7622f4e9abc2d33dc2bb0c7
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Jan 16 17:03:03 2025 +0100

    Fixed issues found by the new indentation/lint tools

commit 0924d62a600706b218c10a9d51ef401a22aa0624
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Jan 22 14:01:59 2025 +0100

    Full reindentation of the code

commit f1a69789548f1c00bebb7b966784790743c25012
Author: Marco Clemencic <marco.clemencic@cern.ch>
Date:   Tue Jan 14 17:19:36 2025 +0100

    fix formatting for RealTel40Format/MCLDstFiles.py

commit 10c024a9a6854311d0342f0e205c9e50af75d7e6
Author: Marco Clemencic <marco.clemencic@cern.ch>
Date:   Tue Jan 14 17:14:57 2025 +0100

    Update clang-format and switch to Ruff for Python linting and formatting

commit 56c53d6979a280b4c23dbb228273d942ffa739d4
Merge: 5af6d311d3 63e24b2d6b
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 21 17:18:15 2025 +0100

    Merge branch 'sponce_fixResChecker' into 'master'

    Fixed Histogram paths in TrackResChecker when split_type is False

    See merge request lhcb/Rec!4234

commit 63e24b2d6baa196593abaa30c89d0e42c2616295
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 21 08:30:22 2025 +0100

    Fixed Histogram paths in TrackResChecker when split_type is False

    It is supposed to use "ALL" and "Unknown" had been introduced when switching to new histograms

commit 5af6d311d31033ed37638fc30c820b7ffff29393
Merge: 6a352902a1 4006a969cd
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Sat Jan 18 21:20:09 2025 +0100

    Merge branch 'improve_interpolator' into 'master'

    Improve track interpolator

    See merge request lhcb/Rec!4230

commit 4006a969cd0b9a04fae2a06344e5066fc0b76596
Author: Ya Zhao <ya.zhao@cern.ch>
Date:   Sat Jan 18 21:20:09 2025 +0100

    Improve track interpolator

commit 6a352902a17d3c4aeeefc082beab8e225079ee49
Merge: daffb32eaa 166032dda6
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Fri Jan 17 10:59:53 2025 +0100

    Merge branch 'ref_bot_LHCb4840_Moore4195' into 'master'

    Update References for: LHCb!4840, Moore!4195 based on lhcb-master-mr/12236

    See merge request lhcb/Rec!4225

commit 5f3e2b47495590bb26ec1203158da8aab067945d
Author: Marco Clemencic <marco.clemencic@cern.ch>
Date:   Tue Jan 14 17:13:15 2025 +0100

    fix for Python3

commit 166032dda65172b96a0b5acbda906baaec655d67
Author: RefBot <lhcbsoft@cern.ch>
Date:   Thu Jan 16 10:07:36 2025 +0100

    Update References for: LHCb!4840, Moore!4195 based on lhcb-master-mr/12236

    [skip ci]

commit daffb32eaac50c33b74d76d3192ec50d0ff2296d
Merge: 9e96e1d3b7 ecf320cb0d
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Jan 15 14:27:58 2025 +0100

    Merge branch 'sponce_DropHistoAlg' into 'master'

    Dropped usage of GaudiHistoAlg in Rec, replace it with new Histograms

    See merge request lhcb/Rec!4200

commit 9e96e1d3b7a9e5b1898360680f4951d6855b1176
Merge: 52540b432f b27cd7e263
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Jan 15 13:37:31 2025 +0100

    Merge branch 'mstahl_velomatch_to_master' into 'master'

    Improvements for matching downstream composites to Velo tracks

    See merge request lhcb/Rec!4141

commit b27cd7e263162aa05932156321e338fee5d51fad
Author: mstahl <marian.stahl@cern.ch>
Date:   Tue Jan 14 17:02:17 2025 +0100

    better handling of particle fitting and extrapolation

commit 52540b432f478641c621fd36e2ccab80c155c293
Merge: 930689bcf4 77f428f8f1
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 14 08:51:50 2025 +0100

    Merge branch 'sponce_fixRef' into 'master'

    Fixed bad ref file for test_extrapolators

    See merge request lhcb/Rec!4222

commit 77f428f8f18ac7fa5922b86abdd0ab93333b92fd
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 14 08:50:48 2025 +0100

    Fixed bad ref file for test_extrapolators

commit 930689bcf4f0019e0d1399c22784f5069e905970
Merge: c21d9fead0 902c88ec93
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Mon Jan 13 17:08:05 2025 +0100

    Merge branch 'sponce_cleanupRef' into 'master'

    Dropped temporary ref files introduced for the move to Gaudi v39r2 and now useless

    See merge request lhcb/Rec!4221

commit 902c88ec931a53e172b2f378521a261e5ea84561
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Mon Jan 13 17:05:28 2025 +0100

    Dropped temporary ref files introduced for the move to Gaudi v39r2 and now useless

commit c21d9fead03d3b2133e62092b5e2e0989cba5621
Merge: 4a2d226053 e398788b47
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Mon Jan 13 17:00:15 2025 +0100

    Merge branch 'sponce_Gaudiv39r2' into 'master'

    Ref update for move to Gaudi v39r2

    See merge request lhcb/Rec!4218

commit e398788b47cfe52be48a524c3e128e02d0da3d69
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Mon Jan 13 10:26:58 2025 +0100

    Ref update for move to Gaudi v39r2

commit 4a2d226053663bd1233a4bafefb3c79136883907
Merge: 8a1c1a7313 50a85cd5ea
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Mon Jan 13 12:55:44 2025 +0100

    Merge branch 'rich-tests-make-root-filename-test-specific' into 'master'

    Make RICH test ROOT filenames test specific

    See merge request lhcb/Rec!4219

commit 8a1c1a731336702d7e8d37a3b6eae60b97c8e0e3
Merge: 6251c857ea 8927e61456
Author: Christopher Rob Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Mon Jan 13 12:50:25 2025 +0100

    Merge branch 'remove-old-gaudi-workarounds-RICH' into 'master'

    RichDetectorHits: Remove workarounds for Gaudi < v39

    See merge request lhcb/Rec!4220

commit 534ef114115d1faa1ff2b5e001cd41c00735387c
Author: mstahl <marian.stahl@cern.ch>
Date:   Sat Nov 23 15:45:13 2024 +0100

    refactor downstream composite to velo track matching

commit 8927e614561a76b074f2497687424d39ca0cb02a
Author: Chris Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Mon Jan 13 10:41:46 2025 +0000

    RichDetectorHits: Remove workarounds for Gaudi < v39

commit 50a85cd5ea1d37de1616399bdef71e2357db9ff1
Author: Chris Jones <jonesc@hep.phy.cam.ac.uk>
Date:   Mon Jan 13 10:30:58 2025 +0000

    Make RICH test ROOT filenames test specific

commit 51012651d28e64fdf9c17f4d1ea3bd93b34097fb
Author: mstahl <marian.stahl@cern.ch>
Date:   Thu Nov 14 13:10:24 2024 +0100

    fix state to particle conversion; adapt to new logic of what is considered basic particle

commit 9c106ce31d2a110908d5e16f3ab62156baa0a648
Author: mstahl <marian.stahl@cern.ch>
Date:   Sun Nov 10 14:17:09 2024 +0100

    First draft of offline hyperon VeloMatching

commit 6251c857ea3e425100decadd3b8ae9de220e6fa6
Merge: 2eb88ae937 8d3ad3db78
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Mon Jan 13 08:54:59 2025 +0100

    Merge branch 'interpolator_PrKalmanFitResult' into 'master'

    adapt interpolator to PrKalmanFitResult

    See merge request lhcb/Rec!4123

commit 8d3ad3db789a546a5246defe8fc4bee0ed6e12f2
Author: Ya Zhao <ya.zhao@cern.ch>
Date:   Mon Jan 13 08:54:59 2025 +0100

    adapt interpolator to PrKalmanFitResult

commit ecf320cb0da91399008fbb5f67d54da9ffc07758
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Fri Jan 10 11:02:10 2025 +0100

    Fixed TriggerObjectsCompatibilityProfileChecker for gaudi v39

commit 13c843d65da5e8eff9fdfe73a86d22320f690ad4
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Dec 17 11:57:35 2024 +0100

    Dropped usage of GaudiHistoAlg in TrackMuonMatchMonitor, use new histograms instead

commit 491d8102212e2d137c2295184161bfbaf5752fdd
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Mon Dec 16 15:10:35 2024 +0100

    Dropped unused TrackV0Monitor

commit a22c171d06a3de6c0dde2c104daf4eef016c6c42
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Fri Dec 13 17:46:42 2024 +0100

    Replaced GaudiHistoAlg with new histograms in TrackResChecker and TrackCheckerBase

commit 68e21b23012ee32cd96b11f4139b7121f83b0d7b
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Dec 12 17:58:30 2024 +0100

    Dropped unused TriggerObjectsCompatibilityChecker

commit e494580768a46e56c20e99bbd0290cb0bd282cdd
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Dec 12 16:59:34 2024 +0100

    Dropped unused TrackEffChecker

commit 22cde50b8ae62924cdfc7247ed56926afc8846aa
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Dec 12 16:51:51 2024 +0100

    Dropped unused ExtrapolatorChecker

commit 37bd4637610b1edc68188213ef30ae6bb65aa470
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Dec 12 16:05:06 2024 +0100

    Dropped unused TrackCloneChecker

commit 7524e49891d0cc7f78b8ebe1e47e4c8edb1524b0
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Dec 12 08:36:08 2024 +0100

    Dropped unused TrackMonitorBase

commit bffd6fec09b5f5476274ef404530400057d66e23
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 11 18:34:38 2024 +0100

    Dropped unused TrackDiMuonMonitor

commit 5d498dd808019610c4861053ce8007c55162fde4
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 11 18:29:46 2024 +0100

    Dropped DEBUGHISTOGRAMS in TrackCloneKiller and TrackBestTrackCreator

    The functionnality is not used anymore and the code behind is obsolete (usage of old histograms)

commit 436cf242731721e1e88a7a64fa417c83386c7749
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 11 14:28:03 2024 +0100

    Dropped unused RecProcessingTimeMoni

commit 2628b523bdc06a9cbf67771f0a6d4b3bfcfc599e
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 11 14:27:08 2024 +0100

    Dropped unused ProcStatAbortMoni

commit f073850a7feca382430e7e0a7fbac8c2a524f73d
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 11 14:21:03 2024 +0100

    Dropped unused EventTimeMonitor

commit df3a750f7d0f92b7932ecd9a186b675723d2490c
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 11 14:15:50 2024 +0100

    Dropped usage of GaudiHistoAlg and use new histograms

commit 2eb88ae937d20dc563a320cc55df49cc86b6c6dd
Merge: e597be061c 0556dc83c5
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Jan 9 15:48:15 2025 +0100

    Merge branch 'sponce_fixref' into 'master'

    Fixed ref files of rich decoding for v3 platforms

    See merge request lhcb/Rec!4214

commit 0556dc83c518c99c9264fdbee0e53eea37aa44a4
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Jan 9 15:44:45 2025 +0100

    Fixed ref files od rich decoding for v3 platforms

commit e597be061c8438a32be052b996905d6bcffdf068
Merge: 7ba7e14249 1d6c582d98
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Jan 9 12:21:36 2025 +0100

    Merge branch 'RichRecon-PIDNtupleCreation-Dec10-2024' into 'master'

    Rich PID ntuple creation software

    See merge request lhcb/Rec!4181

commit 1d6c582d98eecc27077f39e71ec1769afdb837ad
Author: Sajan Easo <sajan.easo@cern.ch>
Date:   Thu Jan 9 12:21:36 2025 +0100

    Rich PID ntuple creation software

commit 7ba7e142499bb9ba26180c18ab7b63f38cb7e7c3
Merge: 6dca5d7842 dd6e88d1ef
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 7 19:07:24 2025 +0100

    Merge branch 'ahennequ_velokalman' into 'master'

    Implements VeloKalman for V1 tracks

    Closes #587

    See merge request lhcb/Rec!4113

commit dd6e88d1efd0a0dbb2c1390d5763b97d9e9090ed
Author: Arthur Marius Hennequin <arthur.hennequin@cern.ch>
Date:   Tue Jan 7 19:07:24 2025 +0100

    Implements VeloKalman for V1 tracks

commit 6dca5d78428904bd19200c873b10e138928d7859
Merge: 68c2c7ad8f 1cdb58c724
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 7 17:24:06 2025 +0100

    Merge branch 'ref_bot_Detector288_LHCb3757_Rec3107_Moore1758' into 'master'

    Update References for: Detector!288, LHCb!3757, Rec!3107, Moore!1758 based on lhcb-master-mr/12160

    See merge request lhcb/Rec!4212

commit 68c2c7ad8f593265695389dfb72f0e19988e1c66
Merge: 10b3ae73d4 3674550c1c
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Jan 7 17:23:42 2025 +0100

    Merge branch 'sponce_calohisto' into 'master'

    Modernized Calo monitoring and dropped MoniAlg

    See merge request lhcb/Rec!3107

commit 1cdb58c72464f5bde657ce3ccc126a957734ea5d
Author: RefBot <lhcbsoft@cern.ch>
Date:   Tue Jan 7 17:05:03 2025 +0100

    Update References for: Detector!288, LHCb!3757, Rec!3107, Moore!1758 based on lhcb-master-mr/12160

    [skip ci]

commit 10b3ae73d4bcba94d965fb0e45bb63cf0647a626
Merge: f63240858f 07bb72185e
Author: Miroslav Saur <miroslav.saur@cern.ch>
Date:   Tue Jan 7 09:21:05 2025 +0100

    Merge branch 'wh_updatevertexmonitor' into 'master'

    Update of TrackVertexMonitor and TrackParticleMonitor for alignment

    See merge request lhcb/Rec!4119

commit 07bb72185e16f7fa3ba5a48fa109ec29f3a62a89
Author: Wouter Hulsbergen <wouterh@nikhef.nl>
Date:   Tue Jan 7 09:21:04 2025 +0100

    Update of TrackVertexMonitor and TrackParticleMonitor for alignment

commit 3674550c1c699e5242a4565440b10557b588d792
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue May 7 18:11:42 2024 +0200

    Modernized CaloFutureLEDMonitor and dropped dependency on CaloFutureMoniAlg

commit d7d2f5acaee597c37800977a1109b1abec586fb1
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Wed Dec 14 12:47:22 2022 +0100

    Moved most of cellid based histo sink code to LHCb

commit 198ce53d4a76da5a3dc0ca53104dcddb904a21f3
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Fri Nov 18 18:37:44 2022 +0100

    Extended CaloHistogramSink to Profile histograms

    Also allows to either store them as is, or convert to a pair of regular histogrms for mean and rms

commit 5da8fddc26b66f3885bd6d3e322f1235b09efb16
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Sep 22 15:25:52 2022 +0200

    Added a new Root histogram Sink for cellId based histograms

    This sink is recreating the original 2D histogram in the output Root file for regular CellID Histograms.
    For CellID histograms with an extra dimension (you can see them as a set of 1D histograms, one per cell),
    a 3D ROOT histogram is created allowing to either extract the 1D histogram for a given cell by fixing the
    first 2 coordinates, or to extract a 2D view of the Calorimeter for a given value of the 3rd parameter.

commit e6d70f0cd467f153641c055684258371dee20b29
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Tue Sep 6 16:45:46 2022 +0200

    Dropped CaloFutureMoniAlg, now unused

commit 04b6002b3d890c7dc241f49862d77b2359e16c81
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Fri Sep 2 14:23:09 2022 +0200

    Modernized CaloFutureTimeAlignment and dropped dependency on CaloFutureMoniAlg

commit 00866f8a4e7d4fe6f00acfb27873d1904de4e7b6
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Sep 1 08:33:58 2022 +0200

    Modernized CaloFuturePedestal and dropped dependency on CaloFutureMoniAlg

commit 20180fba11a443f3aba2bca15d082afb0394d3a2
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Sep 1 10:24:37 2022 +0200

    Modernized CaloFutureDigitMonitor and dropped dependency on CaloFutureMoniAlg

commit 0cb2fb3342a586f4d61b015f70307b7c18b74de7
Author: Sebastien Ponce <sebastien.ponce@cern.ch>
Date:   Thu Sep 1 10:24:29 2022 +0200

    Modernized CaloFutureClusterMonitor  and dropped dependency on CaloFutureMoniAlg
---
 CMakeLists.txt                                |   1 +
 .../src/UpdateVertexCoordinatesOffline.cpp    |   2 +-
 Tr/TrackMonitors/CMakeLists.txt               |   2 +
 Tr/TrackMonitors/src/UTGlobalEffMon.cpp       | 279 ++++++++
 UT/UTMonitors/CMakeLists.txt                  |  39 +
 UT/UTMonitors/src/UTBSMonitor.cpp             | 456 ++++++++++++
 UT/UTMonitors/src/UTDigitsMonitor.cpp         | 668 ++++++++++++++++++
 UT/UTMonitors/src/UTErrorMonitor.cpp          | 351 +++++++++
 UT/UTMonitors/src/UTNZSMonitor.cpp            | 372 ++++++++++
 UT/UTMonitors/src/UTSuperTAEMonitor.cpp       | 179 +++++
 UT/UTMonitors/src/UTTAEMonitor.cpp            | 165 +++++
 .../src/UTVeloUTCorrelationsMonitor.cpp       | 134 ++++
 12 files changed, 2647 insertions(+), 1 deletion(-)
 create mode 100644 Tr/TrackMonitors/src/UTGlobalEffMon.cpp
 create mode 100644 UT/UTMonitors/CMakeLists.txt
 create mode 100644 UT/UTMonitors/src/UTBSMonitor.cpp
 create mode 100644 UT/UTMonitors/src/UTDigitsMonitor.cpp
 create mode 100644 UT/UTMonitors/src/UTErrorMonitor.cpp
 create mode 100644 UT/UTMonitors/src/UTNZSMonitor.cpp
 create mode 100644 UT/UTMonitors/src/UTSuperTAEMonitor.cpp
 create mode 100644 UT/UTMonitors/src/UTTAEMonitor.cpp
 create mode 100644 UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index e39c8b20683..5b33a74c59b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -137,6 +137,7 @@ lhcb_add_subdirectories(
     Phys/FunctorCache
     Utils/CatboostStandaloneEvaluator
     Vis/PhoenixAlgs
+    UT/UTMonitors
 )
 
 gaudi_install(CMAKE
diff --git a/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp b/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp
index 0dc413eaa23..b3b63dc6632 100644
--- a/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp
+++ b/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp
@@ -148,4 +148,4 @@ namespace LHCb {
   };
 
   DECLARE_COMPONENT_WITH_ID( UpdateVertexCoordinatesOffline, "UpdateVertexCoordinatesOffline" )
-} // namespace LHCb
\ No newline at end of file
+} // namespace LHCb
diff --git a/Tr/TrackMonitors/CMakeLists.txt b/Tr/TrackMonitors/CMakeLists.txt
index fffb7419700..f0304ba1e2a 100644
--- a/Tr/TrackMonitors/CMakeLists.txt
+++ b/Tr/TrackMonitors/CMakeLists.txt
@@ -35,6 +35,8 @@ gaudi_add_module(TrackMonitors
         src/TrackVertexMonitor.cpp
         src/TrackVPOverlapMonitor.cpp
         src/UTTrackMonitor.cpp
+        src/UTTrackMonitor.cpp
+        src/UTGlobalEffMon.cpp
         src/VPTrackMonitor.cpp
         src/VPHitEfficiencyMonitor.cpp
         src/VertexCompare.cpp
diff --git a/Tr/TrackMonitors/src/UTGlobalEffMon.cpp b/Tr/TrackMonitors/src/UTGlobalEffMon.cpp
new file mode 100644
index 00000000000..476775432d2
--- /dev/null
+++ b/Tr/TrackMonitors/src/UTGlobalEffMon.cpp
@@ -0,0 +1,279 @@
+/*****************************************************************************\
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "DetDesc/DetectorElement.h"
+#include "Event/FitNode.h"
+#include "Event/PrFitNode.h"
+#include "Event/PrHits.h"
+#include "Event/PrKalmanFitResult.h"
+#include "Event/State.h"
+#include "Event/Track.h"
+#include "Gaudi/Accumulators/Histogram.h"
+#include "GaudiKernel/Algorithm.h"
+#include "GaudiKernel/PhysicalConstants.h"
+#include "Kernel/LHCbID.h"
+#include "Kernel/UTNames.h"
+#include "LHCbAlgs/Consumer.h"
+#include "TrackKernel/TrackFunctors.h"
+#include "TrackMonitorTupleBase.h"
+#include "UTDAQ/UTDAQHelper.h"
+#include "UTDAQ/UTInfo.h"
+#include <algorithm>
+#include <bitset>
+#include <cstdint>
+#include <iomanip>
+#include <string>
+#include <vector>
+
+/** @class UTGlobalEffMon UTGlobalEffMon.h
+ */
+
+using namespace LHCb;
+using namespace Gaudi;
+
+template <typename T>
+class Mutable {
+  mutable T m_t;
+
+public:
+  template <typename... Args>
+    requires std::is_constructible_v<T, Args...>
+  Mutable( Args&&... args ) : m_t{ std::forward<Args>( args )... } {}
+
+  template <typename Arg>
+  decltype( auto ) operator[]( Arg&& arg ) const {
+    return m_t[std::forward<Arg>( arg )];
+  }
+};
+
+namespace {
+  template <class TrackContainer, class Predicate>
+  std::vector<const LHCb::Track*> myselect( const TrackContainer& tracks, Predicate&& selector ) {
+    std::vector<const LHCb::Track*> rc;
+    std::copy_if( tracks.begin(), tracks.end(), std::back_inserter( rc ), std::forward<Predicate>( selector ) );
+    return rc;
+  }
+
+  auto TrackTypePredicate = []( LHCb::Track::Types atype ) {
+    return [=]( const LHCb::Track* track ) { return track->type() == atype; };
+  };
+
+  auto TrackBackwardPredicate = []( const LHCb::Track* track ) { return track->isVeloBackward(); };
+
+  auto TrackForwardPredicate = []() { return [=]( const LHCb::Track* track ) { return !track->isVeloBackward(); }; };
+
+  auto TrackVeloSidePredicate = []( int asign ) {
+    // +1: left side only, 0: overlap track, -1: right side only
+    // for asign > 0 select left-side tracks only, for a < 0 select right-side tracks, reject overlap tracks
+    return [=]( const LHCb::Track* track ) {
+      int  side         = 0;
+      bool allhitsleft  = true;
+      bool allhitsright = true;
+
+      const std::vector<LHCb::LHCbID>& track_ids = track->lhcbIDs();
+      for ( const auto& track_id : track_ids ) {
+        if ( !track_id.isVP() ) continue;
+
+        auto vp_id = track_id.vpID();
+        // 0 should be right, 1 left side
+        bool sidepos = ( vp_id.sidepos() == LHCb::Detector::VPChannelID::Side::A );
+
+        allhitsleft  = allhitsleft && sidepos;
+        allhitsright = allhitsright && !sidepos;
+      }
+      if ( allhitsleft ) side = +1;
+      if ( allhitsright ) side = -1;
+      return side * asign > 0;
+    };
+  };
+
+} // namespace
+
+template <typename OWNER, typename K, typename H, typename A>
+void buildHistogram( OWNER owner, std::map<K, H>& h, K k, std::string name, std::string labels, A axis ) {
+  h.emplace( std::piecewise_construct, std::forward_as_tuple( k ), std::forward_as_tuple( owner, name, labels, axis ) );
+}
+
+template <typename TFitResult, typename TNode>
+class UTGlobalEffMon
+    : public LHCb::Algorithm::Consumer<void( LHCb::Track::Range const&, DetectorElement const& ),
+                                       LHCb::DetDesc::usesBaseAndConditions<TrackMonitorTupleBase, DetectorElement>> {
+
+public:
+  UTGlobalEffMon( const std::string& name, ISvcLocator* pSvcLocator );
+  StatusCode initialize() override;
+  void       operator()( LHCb::Track::Range const& tracks, DetectorElement const& ) const override;
+
+private:
+  mutable Gaudi::Accumulators::Histogram<1> m_h_ntracks{
+      this, "h_ntracks", "Number of long track; Number of long tracks;Events", { 1000, 0, 5000 } };
+  mutable Gaudi::Accumulators::Histogram<1> m_h_nutlay{
+      this, "h_nutlay", "Number of UT layers per long track; Number of UT layers;Number of tracks", { 5, -0.5, 4.5 } };
+  mutable Gaudi::Accumulators::Histogram<1> m_h_nutlay_high_mult{
+      this,
+      "h_nutlay_high_mult",
+      "Number of UT layers per long track high mult ; Number of UT layers;Number of tracks high mult",
+      { 5, -0.5, 4.5 } };
+  //    full-width sensor 2D bins
+  mutable Gaudi::Accumulators::Histogram<2> m_h_all3{ this,
+                                                      "h_all3",
+                                                      "All tracks         Y vs X UTbX-midplane;X [mm];Y [mm]",
+                                                      { { 18, -860.4, +860.4 }, { 14, -669.2, +669.2 } } };
+  mutable Gaudi::Accumulators::Histogram<2> m_h_pass3{ this,
+                                                       "h_pass3",
+                                                       "UT-matched tracks  Y vs X UTbX-midplane;X [mm];Y [mm]",
+                                                       { { 18, -860.4, +860.4 }, { 14, -669.2, +669.2 } } };
+
+  mutable Gaudi::Accumulators::Histogram<2> m_h_pass3_low_mult{
+      this,
+      "h_pass3_low_mult",
+      "UT-matched tracks  Y vs X UTbX-midplane low mult;X [mm];Y [mm]",
+      { { 18, -860.4, +860.4 }, { 14, -669.2, +669.2 } } };
+
+  mutable Gaudi::Accumulators::Histogram<1> m_h_residual_inner{
+      this,
+      "h_residual_inner",
+      "UT track residual - inner detector;residual [mm];number of UT clusters",
+      { 1000, -0.5, 0.5 } };
+  mutable Gaudi::Accumulators::Histogram<1> m_h_residual_outer{
+      this,
+      "h_residual_outer",
+      "UT track residual - outer detector;residual [mm];number of UT clusters",
+      { 1000, -0.5, 0.5 } };
+
+  mutable Gaudi::Accumulators::Histogram<1> m_h_residualpull_inner{
+      this,
+      "h_residualpull_inner",
+      "UT track residual pull - inner detector;pull;number of UT clusters",
+      { 100, -5, 5 } };
+  mutable Gaudi::Accumulators::Histogram<1> m_h_residualpull_outer{
+      this,
+      "h_residualpull_outer",
+      "UT track residual pull - outer detector;pull;number of UT clsuters",
+      { 100, -5, 5 } };
+};
+
+using UTDataTrMonitor          = UTGlobalEffMon<LHCb::TrackFitResult, LHCb::FitNode>;
+using UTDataTrMonitor_PrKalman = UTGlobalEffMon<LHCb::PrKalmanFitResult, LHCb::Pr::Tracks::Fit::Node>;
+DECLARE_COMPONENT_WITH_ID( UTDataTrMonitor, "UTGlobalEffMon" )
+DECLARE_COMPONENT_WITH_ID( UTDataTrMonitor_PrKalman, "UTGlobalEffMon_PrKalman" )
+
+template <typename TFitResult, typename TNode>
+StatusCode UTGlobalEffMon<TFitResult, TNode>::initialize() {
+  return Consumer::initialize().andThen( [&] {} );
+}
+
+template <typename TFitResult, typename TNode>
+UTGlobalEffMon<TFitResult, TNode>::UTGlobalEffMon( const std::string& name, ISvcLocator* pSvcLocator )
+    : Consumer( name, pSvcLocator,
+                { { "TracksInContainer", LHCb::TrackLocation::Default },
+                  { "StandardGeometryTop", LHCb::standard_geometry_top } } ) {}
+
+template <typename TFitResult, typename TNode>
+void UTGlobalEffMon<TFitResult, TNode>::operator()( LHCb::Track::Range const& tracks,
+                                                    const DetectorElement&    lhcb ) const {
+  auto&        geometry        = *lhcb.geometry();
+  const double zutlay3         = 2652.5;
+  const double UT_sensor_width = 95.6; // do not change this because you think this is not correct!
+  int          nTracks         = tracks.size();
+
+  if ( nTracks > 10000 ) { nTracks = 10000; } // saturation
+  ++m_h_ntracks[nTracks];
+
+  for ( const LHCb::Track* track : tracks ) {
+    auto fitResult = dynamic_cast<const TFitResult*>( track->fitResult() );
+    if ( fitResult ) {
+      LHCb::StateVector aState;
+      extrapolator()->propagate( *track, zutlay3, aState, geometry ).ignore();
+      if ( aState.x() < -9990 ) { continue; } // Skip unphysical tracks
+
+      // tracks must be within UT layer 3 acceptance (exclude also generous region around the beam pipe )
+      double tr2utlax3 = aState.x();
+      double tr2utlay3 = aState.y();
+      if ( fabs( tr2utlax3 ) > 9 * UT_sensor_width ) continue;
+      if ( fabs( tr2utlay3 ) > 7 * UT_sensor_width ) continue;
+      if ( sqrt( pow( tr2utlay3, 2 ) + pow( tr2utlax3, 2 ) ) < 45 ) continue;
+
+      // PT cut
+      try {
+
+        auto cState = track->closestToBeamState();
+        if ( cState.pt() < 1000.0 ) continue;
+
+      } catch ( ... ) { continue; }
+
+      // all tracks passing the cuts (for efficiency map denominator)
+      ++m_h_all3[{ tr2utlax3, tr2utlay3 }];
+
+      const double kDummy = 1000.0;
+
+      int    _tr2ut[4]    = { -1, -1, -1, -1 };
+      double _tr2utr[4]   = { kDummy, kDummy, kDummy, kDummy };
+      double _tr2utchi[4] = { 0.0, 0.0, 0.0, 0.0 };
+
+      unsigned int utnode = 0;
+      for ( const auto& node : nodes( *fitResult ) ) {
+        if ( !( node.hasMeasurement() && node.isHitOnTrack() && node.isUT() ) ) continue;
+        if ( node.isOutlier() ) continue;
+        LHCb::LHCbID lhcbID = id( node );
+
+        Detector::UT::ChannelID chan = lhcbID.utID();
+
+        unsigned int chID = static_cast<unsigned int>( chan );
+
+        double       res    = node.residual();
+        double       chi    = res / node.errResidual();
+        double       ares   = fabs( res );
+        unsigned int _layer = ( chID >> 18 ) & 3;
+
+        // Loop over layers
+        if ( ares < fabs( _tr2utr[_layer] ) ) {
+          _tr2utr[_layer]   = res;
+          _tr2utchi[_layer] = chi;
+          _tr2ut[_layer]    = chID;
+        }
+        utnode++;
+
+      } // nodes loop
+
+      unsigned int nlay = 0;
+      for ( int layer = 0; layer < 4; ++layer ) {
+        if ( _tr2ut[layer] != -1 ) {
+          ++nlay;
+          // Extract values from the current layer
+          UInt_t _mo   = ( _tr2ut[layer] >> 10 ) & 7;
+          UInt_t _st   = ( _tr2ut[layer] >> 14 ) & 15;
+          bool   inner = ( _st == 0 ) && ( _mo == 3 || _mo == 4 );
+          if ( inner ) {
+            ++m_h_residual_inner[_tr2utr[layer]];
+            ++m_h_residualpull_inner[_tr2utchi[layer]];
+          } else {
+            ++m_h_residual_outer[_tr2utr[layer]];
+            ++m_h_residualpull_outer[_tr2utchi[layer]];
+          }
+        }
+      }
+
+      // tracks matched to UT hits (for nominator of efficiency map)
+      if ( nlay > 0 ) ++m_h_pass3[{ tr2utlax3, tr2utlay3 }];
+
+      // change it for protons 2025
+      ++m_h_nutlay_high_mult[nlay];
+
+      if ( nTracks > 4 && nTracks <= 100 ) { // you may want to move these limits to be configurable parameters
+
+        // UT layer counting for efficiency and ghost rate estimates
+        ++m_h_nutlay[nlay];
+        if ( nlay > 0 ) ++m_h_pass3_low_mult[{ tr2utlax3, tr2utlay3 }];
+      }
+    }
+  }
+}
diff --git a/UT/UTMonitors/CMakeLists.txt b/UT/UTMonitors/CMakeLists.txt
new file mode 100644
index 00000000000..b5e97abfb01
--- /dev/null
+++ b/UT/UTMonitors/CMakeLists.txt
@@ -0,0 +1,39 @@
+###############################################################################
+# (c) Copyright 2000-2021 CERN for the benefit of the LHCb Collaboration      #
+#                                                                             #
+# This software is distributed under the terms of the GNU General Public      #
+# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
+#                                                                             #
+# In applying this licence, CERN does not waive the privileges and immunities #
+# granted to it by virtue of its status as an Intergovernmental Organization  #
+# or submit itself to any jurisdiction.                                       #
+###############################################################################
+#[=======================================================================[.rst:
+UT/UTMonitors
+-------------
+#]=======================================================================]
+
+gaudi_add_module(UTMonitors
+    SOURCES
+    src/UTDigitsMonitor.cpp
+    src/UTTAEMonitor.cpp
+    src/UTSuperTAEMonitor.cpp
+    src/UTErrorMonitor.cpp
+    src/UTNZSMonitor.cpp
+    src/UTBSMonitor.cpp
+    src/UTVeloUTCorrelationsMonitor.cpp
+    LINK
+        Boost::headers
+        Gaudi::GaudiAlgLib
+        Gaudi::GaudiKernel
+        Gaudi::GaudiUtilsLib
+        LHCb::DAQEventLib
+        LHCb::DigiEvent
+        LHCb::LHCbAlgsLib
+        LHCb::LHCbKernel
+        LHCb::RecEvent
+        LHCb::UTDetLib
+        LHCb::UTKernelLib
+        LHCb::UTTELL1Event
+        ROOT::Hist
+)
diff --git a/UT/UTMonitors/src/UTBSMonitor.cpp b/UT/UTMonitors/src/UTBSMonitor.cpp
new file mode 100644
index 00000000000..c6de3a6d622
--- /dev/null
+++ b/UT/UTMonitors/src/UTBSMonitor.cpp
@@ -0,0 +1,456 @@
+/***************************************************************************** \
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/UTDigit.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "Kernel/UTDAQBoard.h"
+#include "Kernel/UTDAQDefinitions.h"
+#include "Kernel/UTDAQID.h"
+#include "LHCbAlgs/Consumer.h"
+#include "LHCbAlgs/Transformer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+#include "UTDAQ/UTDAQHelper.h"
+#include "UTDAQ/UTInfo.h"
+#include "UTDet/DeUTDetector.h"
+#include <Event/RawBank.h>
+#include <Event/RawEvent.h>
+#include <Gaudi/Accumulators/Histogram.h>
+#include <Gaudi/Accumulators/HistogramArray.h>
+#include <Kernel/IUTReadoutTool.h>
+#include <TMath.h>
+#include <mutex>
+#include <optional>
+#include <string>
+
+/** @class UTBSMonitor UTBSMonitor.cpp
+ * --------------------------------------------------
+ *  Counters for additional monitoring of UT
+ *  The algorithm produces several useful histograms
+ *  @author Wojciech Krupa
+ *  @date   07/06/2024
+ * --------------------------------------------------
+ **/
+
+using namespace LHCb;
+namespace GA = Gaudi::Accumulators;
+
+// Definition of axis
+const GA::Axis<double> AxisADC        = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31
+const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" );  // 0-31
+const GA::Axis<double> AxisDIGIT      = GA::Axis<double>( 101, -0.5, 7000.5, "UTDigits (size of digit container)" );
+const GA::Axis<double> AxisHits       = GA::Axis<double>( 100, -0.5, 1500.5, "Hits" );
+const GA::Axis<double> AxisUAChannels = GA::Axis<double>( 268288, 0, 268288, "GlobalChannel_ID" );
+const GA::Axis<double> AxisUCChannels = GA::Axis<double>( 268288, 268288, 536576, "GlobalChannel_ID" );
+const GA::Axis<double> AxisUAChips    = GA::Axis<double>( 2096, -0.5, 2095.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUCChips    = GA::Axis<double>( 2096, 2095.5, 4191.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisThresholds = GA::Axis<double>( 32, -0.5, 31.5, "Threshold" );
+const GA::Axis<double> AxisChannels   = GA::Axis<double>( 64, -0.5, 63.5, "Channels" );
+const GA::Axis<double> AxisUA_aUChips = GA::Axis<double>( 496, -0.5, 495.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUA_aXChips = GA::Axis<double>( 496, 495.5, 991.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUA_bVChips = GA::Axis<double>( 552, 991.5, 1543.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUA_bXChips = GA::Axis<double>( 552, 1543.5, 2095.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUC_aUChips = GA::Axis<double>( 496, 2095.5, 2591.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUC_aXChips = GA::Axis<double>( 496, 2591.5, 3087.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUC_bVChips = GA::Axis<double>( 552, 3087.5, 3639.5, "GlobalASIC_ID" );
+const GA::Axis<double> AxisUC_bXChips = GA::Axis<double>( 552, 3639.5, 4191.5, "GlobalASIC_ID" );
+
+std::vector<unsigned int>            m_channel_counter( 536577, 0 );
+std::vector<unsigned int>            m_asic_counter( 4192, 0 );
+std::map<unsigned int, unsigned int> m_threshold_counter;
+std::map<unsigned int, unsigned int> m_maxchannel_counter;
+
+// 2D plots
+const double positionasic[4][3] = {
+    { -0.4375, -0.3125, -0.1875 }, // Chip 0
+    { -0.1875, -0.0625, -0.0625 }, // Chip 1
+    { +0.1875, +0.0625, +0.0625 }, // Chip 2
+    { +0.4375, +0.3125, +0.1875 }  // Chip 3
+};
+
+// UT layers
+const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" };
+
+enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 };
+template <masks m>
+[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) {
+  constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) );
+  return ( i & static_cast<unsigned int>( m ) ) >> b;
+}
+
+// Tools for booking histograms
+namespace Utility {
+  template <typename T, typename OWNER>
+  void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1 ) {
+    t.emplace( std::piecewise_construct, std::forward_as_tuple( key ),
+               std::forward_as_tuple( owner, key, title, axis1 ) );
+  }
+} // namespace Utility
+/** ------------------------------------------------------------------------------------------------------------------------ */
+
+class UTBSMonitor
+    : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, DeUTDetector const& ),
+                                       LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> {
+public:
+  UTBSMonitor( const std::string& name, ISvcLocator* svcloc )
+      : Consumer{ name,
+                  svcloc,
+                  { { "InputData", UTDigitLocation::UTDigits },
+                    { "InputErrorData", UTDigitLocation::UTDigits },
+                    { "UTLocation", DeUTDetLocation::location() } } } {}
+  ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" };
+
+  StatusCode initialize() override {
+    return Consumer::initialize().andThen( [&] {
+      // Set the top directory to UT
+      if ( histoTopDir().empty() ) setHistoTopDir( "" );
+
+      for ( const auto& module : UTMap.getModulesNames() ) {
+        for ( unsigned int i = 0; i < 4; i++ )
+          Utility::map_emplace( m_1d_ADCCounter, module + ".Chip" + std::to_string( i ), this,
+                                module + ".Chip" + std::to_string( i ), { 32, -0.5, 31.5 } );
+      }
+    } );
+  }
+
+  void operator()( const UTDigits& digitsCont, const UTDigits& digitserrCont, DeUTDetector const& det ) const override {
+
+    // General counters
+    const unsigned int nDigits = digitsCont.size() + digitserrCont.size();
+
+    if ( msgLevel( MSG::DEBUG ) ) debug() << "----------  Found nDigits: " << nDigits << endmsg;
+    ++m_nDigits[nDigits];
+
+    // Hitmaps plots and ADC
+    std::fill( m_channel_counter.begin(), m_channel_counter.end(), 0 );
+    std::fill( m_asic_counter.begin(), m_asic_counter.end(), 0 );
+
+    // For maxADC plots
+    unsigned int   prev_channel = 0;
+    unsigned int   prev_adc     = 0;
+    LHCb::UTDigit* aDigit_max;
+
+    // UT bank
+    for ( const auto& d : digitsCont ) {
+
+      fillHeatPlots( d );
+
+      // First event
+      if ( prev_channel == 0 ) {
+        prev_channel = d->strip();
+        prev_adc     = d->depositedCharge();
+        aDigit_max   = d;
+        continue;
+      }
+      // If cluster size >1
+      if ( d->strip() - prev_channel == 1 )
+        if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster
+      // End of cluster
+      if ( d->strip() - prev_channel != 1 ) {
+        fillHeatPlots_MAX( aDigit_max );
+        aDigit_max = d;
+      }
+
+      prev_channel = d->strip();
+      prev_adc     = d->depositedCharge();
+    }
+
+    // UTError bank
+    for ( const auto& d : digitserrCont ) {
+
+      fillHeatPlots( d );
+
+      // First event
+      if ( prev_channel == 0 ) {
+        prev_channel = d->strip();
+        prev_adc     = d->depositedCharge();
+        aDigit_max   = d;
+        continue;
+      }
+      // If cluster size >1
+      if ( d->strip() - prev_channel == 1 )
+        if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster
+      // End of cluster
+      if ( d->strip() - prev_channel != 1 ) {
+        fillHeatPlots_MAX( aDigit_max );
+        aDigit_max = d;
+      }
+
+      prev_channel = d->strip();
+      prev_adc     = d->depositedCharge();
+    }
+
+    // Channel hit counters
+    for ( size_t i = 0; i < m_channel_counter.size(); ++i ) {
+      if ( i <= 268288 )
+        hitRateChannels_UA[i] += m_channel_counter[i];
+      else
+        hitRateChannels_UC[i] += m_channel_counter[i];
+    }
+
+    // Chip hit counters
+    for ( size_t i = 0; i < m_asic_counter.size(); ++i ) {
+      if ( i <= 495 ) {
+        hitRateChipAverage_UA_aU[i] += m_asic_counter[i];
+        hitRateChipAverage_UA_aU_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 991 ) {
+        hitRateChipAverage_UA_aX[i] += m_asic_counter[i];
+        hitRateChipAverage_UA_aX_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 1543 ) {
+        hitRateChipAverage_UA_bV[i] += m_asic_counter[i];
+        hitRateChipAverage_UA_bV_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 2095 ) {
+        hitRateChipAverage_UA_bX[i] += m_asic_counter[i];
+        hitRateChipAverage_UA_bX_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 2591 ) {
+        hitRateChipAverage_UC_aU[i] += m_asic_counter[i];
+        hitRateChipAverage_UC_aU_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 3087 ) {
+        hitRateChipAverage_UC_aX[i] += m_asic_counter[i];
+        hitRateChipAverage_UC_aX_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 3639 ) {
+        hitRateChipAverage_UC_bV[i] += m_asic_counter[i];
+        hitRateChipAverage_UC_bV_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      } else if ( i <= 4191 ) {
+        hitRateChipAverage_UC_bX[i] += m_asic_counter[i];
+        hitRateChipAverage_UC_bX_radial[UTMap.getRadialID( i )] += m_asic_counter[i];
+      }
+    }
+
+    std::map<unsigned int, unsigned int> count_th; // for summary plots
+    std::map<unsigned int, unsigned int> count_ch;
+
+    for ( size_t i = 0; i < m_asic_counter.size(); ++i ) {
+      if ( m_asic_counter[i] > 0 ) {
+        if ( m_asic_counter[i] > m_maxchannel_counter[i] ) {
+          m_maxchannel_counter[i] = m_asic_counter[i];
+        } // for profile histo
+        unsigned int channelID =
+            UTMap.getChannel( UTMap.getASICName( i ).substr( 0, UTMap.getASICName( i ).find( '.' ) ) );
+        fillHeatMap( channelID + 128 * std::atoi( &UTMap.getASICName( i ).back() ), m_threshold_counter[i],
+                     m_maxchannel_counter[i], det );
+
+        ++count_th[m_threshold_counter[i]];
+        ++count_ch[m_maxchannel_counter[i]];
+      }
+    }
+
+    for ( const auto& entry : count_th ) { ZS_thresholds[entry.first] += entry.second; }
+    for ( const auto& entry : count_ch ) { ZS_channels[entry.first] += entry.second; }
+  }
+
+private:
+  mutable UTCoordinatesMap UTMap;
+
+  // General plots
+  // Digits counter
+  mutable GA::Histogram<1> m_nDigits{ this, "nDigits", "Total nDigits", AxisDIGIT };
+  // ADC counter
+  mutable GA::Histogram<1> m_nADC{ this, "nADC", "Total nADC", AxisADC_signed };
+
+  // Hitrate counters - channel
+  mutable GA::ProfileHistogram<1> hitRateChannels_UA{ this, "hitRateChannels_UA", "hitRateChannels_UA",
+                                                      AxisUAChannels };
+  mutable GA::ProfileHistogram<1> hitRateChannels_UC{ this, "hitRateChannels_UC", "hitRateChannels_UC",
+                                                      AxisUCChannels };
+
+  // Hitrate counters - chips
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aU{ this, "Average_hitRate_UAaU", "Average_hitRate_UAaU",
+                                                            AxisUA_aUChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aX{ this, "Average_hitRate_UAaX", "Average_hitRate_UAaX",
+                                                            AxisUA_aXChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bV{ this, "Average_hitRate_UAbV", "Average_hitRate_UAbV",
+                                                            AxisUA_bVChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bX{ this, "Average_hitRate_UAbX", "Average_AhitRate_UAbX",
+                                                            AxisUA_bXChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aU{ this, "Average_hitRate_UCaU", "Average_hitRate_UCaU",
+                                                            AxisUC_aUChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aX{ this, "Average_hitRate_UCaX", "Average_hitRate_UCaX",
+                                                            AxisUC_aXChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bV{ this, "Average_hitRate_UCbV", "Average_hitRate_UCbV",
+                                                            AxisUC_bVChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bX{ this, "Average_hitRate_UCbX", "Average_hitRate_UCbX",
+                                                            AxisUC_bXChips };
+
+  // Hitrate counters - chip radial
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aU_radial{ this, "Average_hitRate_UAaU_radial",
+                                                                   "Average_hitRate_UAaU_radial", AxisUA_aUChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aX_radial{ this, "Average_hitRate_UAaX_radial",
+                                                                   "Average_hitRate_UAaX_radial", AxisUA_aXChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bV_radial{ this, "Average_hitRate_UAbV_radial",
+                                                                   "Average_hitRate_UAbV_radial", AxisUA_bVChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bX_radial{ this, "Average_hitRate_UAbX_radial",
+                                                                   "Average_AhitRate_UAbX_radial", AxisUA_bXChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aU_radial{ this, "Average_hitRate_UCaU_radial",
+                                                                   "Average_hitRate_UCaU_radial", AxisUC_aUChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aX_radial{ this, "Average_hitRate_UCaX_radial",
+                                                                   "Average_hitRate_UCaX_radial", AxisUC_aXChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bV_radial{ this, "Average_hitRate_UCbV_radial",
+                                                                   "Average_hitRate_UCbV_radial", AxisUC_bVChips };
+  mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bX_radial{ this, "Average_hitRate_UCbX_radial",
+                                                                   "Average_hitRate_UCbX_radial", AxisUC_bXChips };
+
+  // Special ADC counters - average ADC
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UA_aU{ this, "Average_ADC_UAaU", "Average_ADC_UAaU", AxisUA_aUChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UA_aX{ this, "Average_ADC_UAaX", "Average_ADC_UAaX", AxisUA_aXChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UA_bV{ this, "Average_ADC_UAbV", "Average_ADC_UAbV", AxisUA_bVChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UA_bX{ this, "Average_ADC_UAbX", "Average_ADC_UAbX", AxisUA_bXChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UC_aU{ this, "Average_ADC_UCaU", "Average_ADC_UCaU", AxisUC_aUChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UC_aX{ this, "Average_ADC_UCaX", "Average_ADC_UCaX", AxisUC_aXChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UC_bV{ this, "Average_ADC_UCbV", "Average_ADC_UCbV", AxisUC_bVChips };
+  mutable GA::ProfileHistogram<1> ADCChipAverage_UC_bX{ this, "Average_ADC_UCbX", "Average_ADC_UCbX", AxisUC_bXChips };
+
+  // Hitmap - channel limit & zs threshold per ASIC - 1D
+  mutable GA::ProfileHistogram<1> ZS_thresholds{ this, "ZS_thresholds", "ZS_thresholds", AxisThresholds };
+  mutable GA::ProfileHistogram<1> ZS_channels{ this, "ZS_channels", "ZS_channels", AxisChannels };
+
+  // Hitmap - channel limit & zs threshold per ASIC - 2D
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_2d_ZS_th_chips{
+      this,
+      []( int i ) { return fmt::format( "UT_ZS_th_Chips_Layer{}", i ); },
+      []( int i ) { return fmt::format( "zs_th of Layer{}", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_2d_ZS_ch_chips{
+      this,
+      []( int i ) { return fmt::format( "UT_ZS_ch_Chips_Layer{}", i ); },
+      []( int i ) { return fmt::format( "zs_channel of Layer{}", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // The container for pedestal per channel
+  mutable std::map<std::string, GA::Histogram<1>> m_1d_ADCCounter;
+
+  void fillHeatPlots( const LHCb::UTDigit* aDigit ) const {
+
+    // Let's find where we are
+    std::tuple<float, float, float, float, std::string, std::string> tuple =
+        UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+    std::string module_name = std::get<4>( tuple );
+    std::string type        = std::get<5>( tuple );
+
+    // What a beautiful line of code
+    unsigned int globalASIC_ID = UTMap.getASICNumber( UT_layers[aDigit->layer()] + "_" + module_name + ".Chip" +
+                                                      std::to_string( aDigit->strip() / 128 ) );
+    ++m_channel_counter[globalASIC_ID * 128 + aDigit->strip() % 128];
+    ++m_asic_counter[globalASIC_ID];
+
+    if ( globalASIC_ID <= 495 )
+      ADCChipAverage_UA_aU[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 991 )
+      ADCChipAverage_UA_aX[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 1543 )
+      ADCChipAverage_UA_bV[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 2095 )
+      ADCChipAverage_UA_bX[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 2591 )
+      ADCChipAverage_UC_aU[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 3087 )
+      ADCChipAverage_UC_aX[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 3639 )
+      ADCChipAverage_UC_bV[globalASIC_ID] += aDigit->depositedCharge();
+    else if ( globalASIC_ID <= 4191 )
+      ADCChipAverage_UC_bX[globalASIC_ID] += aDigit->depositedCharge();
+
+    if ( m_threshold_counter[globalASIC_ID] == 0 )
+      m_threshold_counter[globalASIC_ID] = 31; // we can't start from zero to minimise and we can't init all asics with
+                                               // 31 because we want keep disabled empty
+
+    if ( aDigit->depositedCharge() < m_threshold_counter[globalASIC_ID] )
+      m_threshold_counter[globalASIC_ID] = aDigit->depositedCharge();
+
+    // ADC counter
+    ++m_nADC[aDigit->depositedCharge()];
+  }
+
+  void fillHeatPlots_MAX( const LHCb::UTDigit* aDigit ) const {
+    // Let's find where we are
+    std::tuple<float, float, float, float, std::string, std::string> tuple =
+        UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+    std::string module_name = std::get<4>( tuple );
+    std::string type        = std::get<5>( tuple );
+    ++m_1d_ADCCounter.at( UT_layers[aDigit->layer()] + "_" + module_name + ".Chip" +
+                          std::to_string( aDigit->strip() / 128 ) )[aDigit->depositedCharge()];
+  }
+
+  void fillHeatMap( unsigned int channel, unsigned int zs_th, unsigned int ch, DeUTDetector const& det ) const {
+    Detector::UT::ChannelID channelID( channel );
+
+    // Let's find where we are
+    auto tuple =
+        UTMap.getTuple( channelID.module(), channelID.face(), channelID.stave(), channelID.side(), channelID.sector() );
+
+    float       x           = std::get<0>( tuple );
+    float       y           = std::get<1>( tuple );
+    std::string module_name = std::get<4>( tuple );
+    std::string type        = std::get<5>( tuple );
+    auto        aSector     = det.findSector( channelID );
+
+#ifdef USE_DD4HEP
+    auto trajStrip0   = aSector.createTraj( 0, 0 );
+    auto trajStrip511 = aSector.createTraj( 511, 0 );
+    auto trajStrip127 = aSector.createTraj( 127, 0 );
+#else
+    auto trajStrip0   = aSector->trajectory( channelID, 0 );
+    auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ channelID + 511 }, 0 );
+    auto trajStrip127 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ channelID + 127 }, 0 );
+#endif
+    double stripx0   = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5;
+    double stripx511 = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5;
+    double stripx127 = -1 * ( trajStrip127.endPoint().x() + trajStrip127.beginPoint().x() ) * 0.5;
+    double stripy    = ( trajStrip0.endPoint().y() + trajStrip0.beginPoint().y() ) * 0.5;
+
+    double stripwidth  = stripx511 - stripx0;
+    double stripwidth2 = stripx127 - stripx0;
+    int    chip;
+
+    if ( stripwidth > 0 )
+      chip = int( channelID.strip() / 128 );
+    else
+      chip = 3 - int( channelID.strip() / 128 );
+
+    double positionasic_one = positionasic[chip][0];
+    double positionasic_two = positionasic[chip][1];
+    double positionasic_thr = positionasic[chip][2];
+
+    // CHIPS Maps //
+    if ( ( channelID.stave() > 1 ) || ( fabs( y ) > 2 ) ) {
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_one, y + 0.25 }] += zs_th;
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_one, y - 0.25 }] += zs_th;
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_two, y + 0.25 }] += zs_th;
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_two, y - 0.25 }] += zs_th;
+      if ( ch > 0 ) {
+        m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_one, y + 0.25 }] += ch;
+        m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_one, y - 0.25 }] += ch;
+        m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_two, y + 0.25 }] += ch;
+        m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_two, y - 0.25 }] += ch;
+      }
+    }
+    if ( ( ( channelID.stave() == 1 ) && ( fabs( y ) < 2 ) ) ||
+         ( ( channelID.stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) {
+
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_thr, y + 0.25 }] += zs_th;
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_thr, y - 0.25 }] += zs_th;
+      if ( ch > 0 ) {
+        m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_thr, y + 0.25 }] += ch;
+        m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_thr, y - 0.25 }] += ch;
+      }
+    }
+    if ( ( channelID.stave() == 0 ) && ( fabs( y ) < 1 ) ) {
+      m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_thr, y }] += zs_th;
+      if ( ch > 0 ) m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_thr, y }] += ch;
+    }
+  }
+
+}; // End of class UTBSMonitor
+DECLARE_COMPONENT( UTBSMonitor )
diff --git a/UT/UTMonitors/src/UTDigitsMonitor.cpp b/UT/UTMonitors/src/UTDigitsMonitor.cpp
new file mode 100644
index 00000000000..f1bfd7fe03f
--- /dev/null
+++ b/UT/UTMonitors/src/UTDigitsMonitor.cpp
@@ -0,0 +1,668 @@
+/***************************************************************************** \
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/UTDigit.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "Kernel/UTDAQBoard.h"
+#include "Kernel/UTDAQDefinitions.h"
+#include "Kernel/UTDAQID.h"
+#include "LHCbAlgs/Consumer.h"
+#include "LHCbAlgs/Transformer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+#include "UTDAQ/UTDAQHelper.h"
+#include "UTDAQ/UTInfo.h"
+#include "UTDet/DeUTDetector.h"
+#include <Event/ODIN.h>
+#include <Event/RawBank.h>
+#include <Event/RawEvent.h>
+#include <Gaudi/Accumulators/Histogram.h>
+#include <Gaudi/Accumulators/HistogramArray.h>
+#include <Kernel/IUTReadoutTool.h>
+#include <mutex>
+#include <optional>
+#include <string>
+
+/** @class UTDigitsMonitor UTDigitsMonitor.cpp
+ * --------------------------------------------------
+ *  Counters for Monet online monitoring of the UT
+ *  The algorithm produces several useful histograms
+ *  @author Wojciech Krupa
+ *  @date   2024/03/20
+ * --------------------------------------------------
+ **/
+
+using namespace LHCb;
+namespace GA = Gaudi::Accumulators;
+
+// Definition of axis
+const GA::Axis<double> AxisADC        = GA::Axis<double>( 64, -32.5, 31.5, "nADC" );       //-32, 31
+const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" );        // 0-31
+const GA::Axis<double> AxisBCID       = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); //
+const GA::Axis<double> AxisDIGIT      = GA::Axis<double>( 1000, -0.5, 100000.5, "UTDigits (size of digit container)" );
+const GA::Axis<double> AxisXnaturalunits   = GA::Axis<double>( 36, -9, 9, "Staves (Sectors)" );
+const GA::Axis<double> AxisXnaturalunits4x = GA::Axis<double>( 144, -9, 9, "Staves (ASICs)" );
+const GA::Axis<double> AxisYnaturalunits   = GA::Axis<double>( 28, -7, 7, "Modules" );
+
+// Definition of histogram names
+const std::array<std::string, 4> s_HistNames = { "UTA_Tell_40_vs_ASIC_Flow0", "UTA_Tell_40_vs_ASIC_Flow1",
+                                                 "UTC_Tell_40_vs_ASIC_Flow0", "UTC_Tell_40_vs_ASIC_Flow1" };
+
+const std::array<std::string, 4> s_HistNames_Counter = {
+    "UTA_Tell_40_EventsCounter_Flow0", "UTA_Tell_40_EventsCounter_Flow1", "UTC_Tell_40_EventsCounter_Flow0",
+    "UTC_Tell_40_EventsCounter_Flow1" };
+
+const std::array<std::string, 6> s_HistNames_BXType = { "nADC", "nADC_A", "nADC_B", "nADC_C", "nADC_D" };
+
+const std::array<std::string, 6> s_HistNames_BXType_labels = {
+    "Total nADC", "Total nADC - type A sensors", "Total nADC - type B sensors", "Total nADC - type C sensors",
+    "Total nADC - type D sensors" };
+
+const std::array<std::string, 15> s_HistNames_EventType = { "NoBias",
+                                                            "Lumi",
+                                                            "Isolated",
+                                                            "Leading",
+                                                            "Trailing",
+                                                            "Empty before isolated",
+                                                            "Empty after isolated",
+                                                            "Empty before leading",
+                                                            "Empty after trailing",
+                                                            "Empty after 19 clock from trailling",
+                                                            "NoBias bb",
+                                                            "Lumi bb",
+                                                            "Isolated bb",
+                                                            "Leading bb",
+                                                            "Trailing bb" };
+
+const std::array<std::string, 15> s_HistNames_EventType_labels = { "NoBias",
+                                                                   "Lumi",
+                                                                   "Isolated - ADC",
+                                                                   "Leading - ADC",
+                                                                   "Trailing - ADC",
+                                                                   "Empty before isolated - ADC",
+                                                                   "Empty after isolated - ADC",
+                                                                   "Empty before leading - ADC",
+                                                                   "Empty after trailing -  ADC",
+                                                                   "Empty after 19 clock from trailling - ADC",
+                                                                   "NoBias bb - ADC",
+                                                                   "Lumi bb - ADC",
+                                                                   "Isolated bb - ADC",
+                                                                   "Leading bb - ADC",
+                                                                   "Trailing bb - ADC" };
+
+// UT layers
+const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" };
+
+// 2D plots
+const double positionasic[4][3] = {
+    { -0.4375, -0.3125, -0.1875 }, // Chip 0
+    { -0.1875, -0.0625, -0.0625 }, // Chip 1
+    { +0.1875, +0.0625, +0.0625 }, // Chip 2
+    { +0.4375, +0.3125, +0.1875 }  // Chip 3
+};
+
+enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 };
+
+template <masks m>
+[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) {
+  constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) );
+  return ( i & static_cast<unsigned int>( m ) ) >> b;
+}
+
+/** ------------------------------------------------------------------------------------------------------------------------ */
+
+class UTDigitsMonitor
+    : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, DeUTDetector const&, LHCb::ODIN const& ),
+                                       LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> {
+public:
+  UTDigitsMonitor( const std::string& name, ISvcLocator* svcloc )
+      : Consumer{ name,
+                  svcloc,
+                  { { "InputData", UTDigitLocation::UTDigits },
+                    { "InputErrorData", UTDigitLocation::UTDigits },
+                    { "UTLocation", DeUTDetLocation::location() },
+                    { "ODINLocation", LHCb::ODINLocation::Default } } } {}
+  ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" };
+
+  StatusCode initialize() override {
+    return Consumer::initialize().andThen( [&] {
+      // Set the top directory to UT
+      if ( histoTopDir().empty() ) setHistoTopDir( "" );
+    } );
+  }
+
+  void operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont, DeUTDetector const& det,
+                   LHCb::ODIN const& odin ) const override {
+
+    // General counters
+    const unsigned int nDigits = digitsCont.size() + errdigitsCont.size();
+    const unsigned int nBuncId = odin.bunchId();
+
+    if ( msgLevel( MSG::DEBUG ) ) debug() << "----------  Found nDigits: " << nDigits << endmsg;
+    ++m_nDigits[nDigits];
+    ++m_nBuncId[nBuncId];
+    ++m_nDigitsnBuncId[{ nBuncId, nDigits }];
+
+    if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+      ++m_nDigits_bb[nDigits];
+    else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+      ++m_nDigits_be[nDigits];
+    else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+      ++m_nDigits_eb[nDigits];
+    else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+      ++m_nDigits_ee[nDigits];
+
+    // -------------------------------------------------------
+    // Loop on digits
+    // side==1 is Aside (left), side==0 is Cside (right)
+    // -------------------------------------------------------
+    // Hitmaps plots and ADC
+
+    // Commisioning per side histograms
+    unsigned int ua_size       = 0;
+    unsigned int uc_size       = 0;
+    unsigned int ua_error_size = 0;
+    unsigned int uc_error_size = 0;
+
+    // For max ADC plots
+    unsigned int   prev_channel = 0;
+    unsigned int   prev_adc     = 0;
+    LHCb::UTDigit* aDigit_max;
+
+    // UT bank
+    for ( const auto& d : digitsCont ) {
+      fillHeatMap( d, det );
+      fillADC( d, odin );
+
+      if ( d->face() == 0 )
+        uc_size++;
+      else if ( d->face() == 1 )
+        ua_size++;
+
+      UTDAQID      daqID    = readoutTool->channelIDToDAQID( d->channelID() );
+      auto         board_ID = daqID.board();
+      unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID );
+
+      if ( sourceID != prevSourceID ) { fillBankHistograms( sourceID ); }
+      prevSourceID = sourceID;
+
+      // Cluster max ADC counters
+      // First event
+      if ( prev_channel == 0 ) {
+        prev_channel = d->strip();
+        prev_adc     = d->depositedCharge();
+        aDigit_max   = d;
+        continue;
+      }
+      // If cluster size >1
+      if ( d->strip() - prev_channel == 1 )
+        if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster
+      // End of cluster
+      if ( d->strip() - prev_channel != 1 ) {
+        fillADC_clusterMAX( aDigit_max );
+        aDigit_max = d;
+      }
+
+      prev_channel = d->strip();
+      prev_adc     = d->depositedCharge();
+    }
+
+    // UT error bank
+    for ( const auto& d : errdigitsCont ) {
+      fillHeatMap( d, det );
+      fillADC( d, odin );
+
+      if ( d->face() == 0 )
+        uc_error_size++;
+      else if ( d->face() == 1 )
+        ua_error_size++;
+
+      UTDAQID      daqID    = readoutTool->channelIDToDAQID( d->channelID() );
+      auto         board_ID = daqID.board();
+      unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID );
+
+      if ( sourceID != prevSourceID ) { fillBankHistograms( sourceID ); }
+
+      prevSourceID = sourceID;
+
+      // Cluster max ADC counters
+      // First event
+      if ( prev_channel == 0 ) {
+        prev_channel = d->strip();
+        prev_adc     = d->depositedCharge();
+        aDigit_max   = d;
+        continue;
+      }
+      // If cluster size >1
+      if ( d->strip() - prev_channel == 1 )
+        if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster
+      // End of cluster
+      if ( d->strip() - prev_channel != 1 ) {
+        fillADC_clusterMAX( aDigit_max );
+        aDigit_max = d;
+      }
+
+      prev_channel = d->strip();
+      prev_adc     = d->depositedCharge();
+    }
+
+    // Commisioning per side histograms
+    ++m_nDigits_UA[ua_size + ua_error_size];
+    ++m_nDigits_UC[uc_size + uc_error_size];
+  }
+
+private:
+  mutable UTCoordinatesMap UTMap;
+  mutable unsigned int     prevSourceID = 0;
+
+  // General plots
+  mutable GA::Histogram<1> m_nBuncId{ this, "nBuncId", "Bunch IDs", AxisBCID };
+  mutable GA::Histogram<2> m_nDigitsnBuncId{ this, "nDigitsnBuncId", "nBuncId vs BunchIDs", AxisBCID, AxisDIGIT };
+
+  // Digits counters
+  mutable GA::Histogram<1> m_nDigits{ this, "nDigits", "Total nDigits", AxisDIGIT };
+  mutable GA::Histogram<1> m_nDigits_UA{ this, "nDigits_UA", "Total nDigits UA", AxisDIGIT };
+  mutable GA::Histogram<1> m_nDigits_UC{ this, "nDigits_UC", "Total nDigits UC", AxisDIGIT };
+
+  // Split for event type
+  mutable GA::Histogram<1> m_nDigits_bb{ this, "nDigits_bb", "Total nDigits bb", AxisDIGIT };
+  mutable GA::Histogram<1> m_nDigits_be{ this, "nDigits_be", "Total nDigits be", AxisDIGIT };
+  mutable GA::Histogram<1> m_nDigits_eb{ this, "nDigits_eb", "Total nDigits eb", AxisDIGIT };
+  mutable GA::Histogram<1> m_nDigits_ee{ this, "nDigits_ee", "Total nDigits ee", AxisDIGIT };
+
+  // ADC counters
+  mutable GA::Histogram<1> m_nADC{ this, "nADC", "Total nADC", AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_A{ this, "nADC_A", "Total nADC - type A sensors", AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_B{ this, "nADC_B", "Total nADC - type B sensors", AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_C{ this, "nADC_C", "Total nADC - type C sensors", AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_D{ this, "nADC_D", "Total nADC - type D sensors", AxisADC_signed };
+
+  // Cluster max counters
+  mutable GA::Histogram<1> m_nADC_clusterMAX{ this, "nADC_clusterMAX", "Total nADC", AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_A_clusterMAX{ this, "nADC_A_clusterMAX", "Total nADC - type A sensors",
+                                                AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_B_clusterMAX{ this, "nADC_B_clusterMAX", "Total nADC - type B sensors",
+                                                AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_C_clusterMAX{ this, "nADC_C_clusterMAX", "Total nADC - type C sensors",
+                                                AxisADC_signed };
+  mutable GA::Histogram<1> m_nADC_D_clusterMAX{ this, "nADC_D_clusterMAX", "Total nADC - type D sensors",
+                                                AxisADC_signed };
+
+  // Split for A/C side (comissioning)
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_ua{
+      this,
+      []( int i ) { return fmt::format( "{}", "UA_" + s_HistNames_BXType[i] ); },
+      []( int i ) { return fmt::format( "{}", "UA " + s_HistNames_BXType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_uc{
+      this,
+      []( int i ) { return fmt::format( "{}", "UC_" + s_HistNames_BXType[i] ); },
+      []( int i ) { return fmt::format( "{}", "UC " + s_HistNames_BXType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_bb{
+      this,
+      []( int i ) { return fmt::format( "{}_bb", s_HistNames_BXType[i] ); },
+      []( int i ) { return fmt::format( "{}_bb", s_HistNames_BXType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_be{
+      this,
+      []( int i ) { return fmt::format( "{}_be", s_HistNames_BXType[i] ); },
+      []( int i ) { return fmt::format( "{}_be", s_HistNames_BXType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_eb{
+      this,
+      []( int i ) { return fmt::format( "{}_eb", s_HistNames_BXType[i] ); },
+      []( int i ) { return fmt::format( "{}_eb", s_HistNames_BXType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_ee{
+      this,
+      []( int i ) { return fmt::format( "{}_ee", s_HistNames_BXType[i] ); },
+      []( int i ) { return fmt::format( "{}_ee", s_HistNames_BXType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 15> m_1d_type{
+      this,
+      []( int i ) { return fmt::format( "{}", s_HistNames_EventType[i] ); },
+      []( int i ) { return fmt::format( "{}", s_HistNames_EventType_labels[i] ); },
+      { 32, -0.5, 31.5, "nADC" } };
+
+  mutable GA::Histogram<2> m_nADCvsBCID{ this, "nADCvsBuncId", "nADC vs BuncId", AxisBCID, AxisADC_signed };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_2d_hitmap_sectors{
+      this,
+      []( int i ) { return fmt::format( "UT_HitMap_Sectors_Layer{}", i ); },
+      []( int i ) { return fmt::format( "Sectors of Layer{} (ASICs)", i ); },
+      { 36, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // UT Hitmaps per asic physical position
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_2d_hitmap_chips{
+      this,
+      []( int i ) { return fmt::format( "UT_HitMap_Chips_Layer{}", i ); },
+      []( int i ) { return fmt::format( "ASICs of Layer{}", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_2d_hitmap_tell40_eff{
+      this,
+      []( int i ) { return fmt::format( "UT_HitMap_TELL40_Eff_Layer{}", i ); },
+      []( int i ) { return fmt::format( "TELL40 of Layer{} (ASICs)", i ); },
+      { 36, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // TELL40 plots
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistNames.size()>
+      m_UT_Tell_40_vs_asic{ this,
+                            []( int i ) { return fmt::format( "{}", s_HistNames[i] ); },
+                            []( int i ) { return fmt::format( "{}", s_HistNames[i] ); },
+                            { 54, 0, 54 },
+                            { 24, 0, 24 } };
+
+  // TELL40 plots - counter
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, s_HistNames_Counter.size()>
+      m_UT_Tell_40_events{ this,
+                           []( int i ) { return fmt::format( "{}", s_HistNames_Counter[i] ); },
+                           []( int i ) { return fmt::format( "{}", s_HistNames_Counter[i] ); },
+                           { 54, 0, 54 } };
+
+  void fillBankHistograms( const int sourceID ) const {
+
+    int SourceID_ = sourceID / 1000;
+
+    if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) {
+      if ( SourceID_ == 10 ) {
+        ++m_UT_Tell_40_events[0][UTMap.getTELL40Bin( sourceID )];
+      } else if ( SourceID_ == 11 ) {
+        ++m_UT_Tell_40_events[1][UTMap.getTELL40Bin( sourceID )];
+      }
+
+    } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) {
+      if ( SourceID_ == 12 ) {
+        ++m_UT_Tell_40_events[2][UTMap.getTELL40Bin( sourceID )];
+      } else if ( SourceID_ == 13 ) {
+        ++m_UT_Tell_40_events[3][UTMap.getTELL40Bin( sourceID )];
+      }
+    }
+
+    auto modules = UTMap.getNamesfromTELL40( UTMap.getTELL40( sourceID ) );
+    for ( const auto& module : modules ) {
+      unsigned int channel = UTMap.getChannel( module );
+
+      Detector::UT::ChannelID channelID( channel );
+
+      // Let's find where we are
+      auto tuple = UTMap.getTuple( channelID.module(), channelID.face(), channelID.stave(), channelID.side(),
+                                   channelID.sector() );
+
+      float       x           = std::get<0>( tuple );
+      float       y           = std::get<1>( tuple );
+      std::string module_name = std::get<4>( tuple );
+      std::string type        = std::get<5>( tuple );
+
+      // SECTORS Maps //
+      if ( ( channelID.stave() > 1 ) || ( fabs( y ) > 2 ) ) {
+        ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x + 0.25, y + 0.25 }];
+        ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x + 0.25, y - 0.25 }];
+        ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x - 0.25, y + 0.25 }];
+        ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x - 0.25, y - 0.25 }];
+      }
+      if ( ( ( channelID.stave() == 1 ) && ( fabs( y ) < 2 ) ) ||
+           ( ( channelID.stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) {
+        ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x, y + 0.25 }];
+        ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x, y - 0.25 }];
+      }
+      if ( ( channelID.stave() == 0 ) && ( fabs( y ) < 1 ) ) { ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x, y }]; }
+    }
+  }
+
+  void fillADC( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin ) const {
+
+    ++m_nADCvsBCID[{ odin.bunchId(), aDigit->depositedCharge() }];
+    std::string type = std::get<5>(
+        UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ) );
+
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::NoBias ) ) ++m_1d_type[0][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::Lumi ) ) ++m_1d_type[1][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_08 ) ) ++m_1d_type[2][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_09 ) ) ++m_1d_type[3][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_10 ) ) ++m_1d_type[4][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_11 ) ) ++m_1d_type[5][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_12 ) ) ++m_1d_type[6][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_13 ) ) ++m_1d_type[7][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_14 ) ) ++m_1d_type[8][aDigit->depositedCharge()];
+    if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_15 ) ) ++m_1d_type[9][aDigit->depositedCharge()];
+
+    if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) {
+      if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::NoBias ) ) ++m_1d_type[10][aDigit->depositedCharge()];
+      if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::Lumi ) ) ++m_1d_type[11][aDigit->depositedCharge()];
+      if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_08 ) ) ++m_1d_type[12][aDigit->depositedCharge()];
+      if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_09 ) ) ++m_1d_type[13][aDigit->depositedCharge()];
+      if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_10 ) ) ++m_1d_type[14][aDigit->depositedCharge()];
+    }
+
+    ++m_nADC[aDigit->depositedCharge()];
+
+    if ( aDigit->face() == 0 ) {
+      ++m_1d_uc[0][aDigit->depositedCharge()];
+      if ( type == "A" ) ++m_1d_uc[1][aDigit->depositedCharge()];
+      if ( type == "B" ) ++m_1d_uc[2][aDigit->depositedCharge()];
+      if ( type == "C" ) ++m_1d_uc[3][aDigit->depositedCharge()];
+      if ( type == "D" ) ++m_1d_uc[4][aDigit->depositedCharge()];
+    } else if ( aDigit->face() == 1 ) {
+      ++m_1d_ua[0][aDigit->depositedCharge()];
+      if ( type == "A" ) ++m_1d_ua[1][aDigit->depositedCharge()];
+      if ( type == "B" ) ++m_1d_ua[2][aDigit->depositedCharge()];
+      if ( type == "C" ) ++m_1d_ua[3][aDigit->depositedCharge()];
+      if ( type == "D" ) ++m_1d_ua[4][aDigit->depositedCharge()];
+    }
+
+    if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+      ++m_1d_bb[0][aDigit->depositedCharge()];
+    else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+      ++m_1d_be[0][aDigit->depositedCharge()];
+    else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+      ++m_1d_eb[0][aDigit->depositedCharge()];
+    else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+      ++m_1d_ee[0][aDigit->depositedCharge()];
+
+    if ( type == "A" ) {
+      ++m_nADC_A[aDigit->depositedCharge()];
+      if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+        ++m_1d_bb[1][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+        ++m_1d_be[1][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+        ++m_1d_eb[1][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+        ++m_1d_ee[1][aDigit->depositedCharge()];
+    } else if ( type == "B" ) {
+      ++m_nADC_B[aDigit->depositedCharge()];
+      if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+        ++m_1d_bb[2][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+        ++m_1d_be[2][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+        ++m_1d_eb[2][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+        ++m_1d_ee[2][aDigit->depositedCharge()];
+    } else if ( type == "C" ) {
+      ++m_nADC_C[aDigit->depositedCharge()];
+      if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+        ++m_1d_bb[3][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+        ++m_1d_be[3][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+        ++m_1d_eb[3][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+        ++m_1d_ee[3][aDigit->depositedCharge()];
+    } else if ( type == "D" ) {
+      ++m_nADC_D[aDigit->depositedCharge()];
+      if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+        ++m_1d_bb[4][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+        ++m_1d_be[4][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+        ++m_1d_eb[4][aDigit->depositedCharge()];
+      else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+        ++m_1d_ee[4][aDigit->depositedCharge()];
+    }
+
+    Detector::UT::ChannelID channelID = aDigit->channelID();
+    UTDAQID                 daqID     = readoutTool->channelIDToDAQID( channelID );
+    auto                    board_ID  = daqID.board();
+    unsigned int            sourceID  = UTMap.getSourceIDfromBoardNumber( board_ID );
+    int                     SourceID_ = sourceID / 1000;
+
+    unsigned int asic_times_lane = int( aDigit->strip() / 128 ) + 4 * extract<masks::lane>( aDigit->getdaqID() );
+
+    // Sadly we (I) can't do it better. But at least its ok!
+    if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) {
+      if ( SourceID_ == 10 ) {
+        if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+          ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+        } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) {
+          ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }];
+        } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) {
+          ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+        }
+      } else if ( SourceID_ == 11 ) {
+        if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+          if ( asic_times_lane == 16 || asic_times_lane == 17 )
+            ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+          else
+            ++m_UT_Tell_40_vs_asic[1][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }];
+        } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" )
+          ++m_UT_Tell_40_vs_asic[1][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }];
+        else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                  UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                  UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" )
+          ++m_UT_Tell_40_vs_asic[1][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+      }
+
+    } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) {
+      if ( SourceID_ == 12 ) {
+        if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+          ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+        } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) {
+          ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }];
+        } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) {
+          ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+        }
+      }
+
+      else if ( SourceID_ == 13 ) {
+        if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+          if ( asic_times_lane == 16 || asic_times_lane == 17 )
+            ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+          else
+            ++m_UT_Tell_40_vs_asic[3][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }];
+        } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" )
+          ++m_UT_Tell_40_vs_asic[3][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }];
+        else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                  UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                  UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" )
+          ++m_UT_Tell_40_vs_asic[3][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+      }
+    }
+  }
+
+  // Fill clusterMAX counters
+  void fillADC_clusterMAX( const LHCb::UTDigit* aDigit ) const {
+
+    std::string type = std::get<5>(
+        UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ) );
+
+    ++m_nADC_clusterMAX[aDigit->depositedCharge()];
+
+    if ( type == "A" ) {
+      ++m_nADC_A_clusterMAX[aDigit->depositedCharge()];
+    } else if ( type == "B" ) {
+      ++m_nADC_B_clusterMAX[aDigit->depositedCharge()];
+    } else if ( type == "C" ) {
+      ++m_nADC_C_clusterMAX[aDigit->depositedCharge()];
+    } else if ( type == "D" ) {
+      ++m_nADC_D_clusterMAX[aDigit->depositedCharge()];
+    }
+  }
+
+  void fillHeatMap( const LHCb::UTDigit* aDigit, DeUTDetector const& det ) const {
+
+    // Let's find where we are
+    std::tuple<float, float, float, float, std::string, std::string> tuple =
+        UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+    float       x           = std::get<0>( tuple );
+    float       y           = std::get<1>( tuple );
+    std::string module_name = std::get<4>( tuple );
+    std::string type        = std::get<5>( tuple );
+    auto        chanID      = aDigit->channelID();
+    auto        aSector     = det.findSector( chanID );
+
+// This may be useful but allows to get only position of hybrind. It can be used to get ASIC position.
+#ifdef USE_DD4HEP
+    auto trajStrip0   = aSector.createTraj( 0, 0 );
+    auto trajStrip511 = aSector.createTraj( 511, 0 );
+#else
+    auto trajStrip0   = aSector->trajectory( chanID, 0 );
+    auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ chanID + 511 }, 0 );
+#endif
+    double stripx0    = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5;
+    double stripx511  = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5;
+    double stripwidth = stripx511 - stripx0;
+    int    chip;
+
+    if ( stripwidth > 0 )
+      chip = int( aDigit->strip() / 128 );
+    else
+      chip = 3 - int( aDigit->strip() / 128 );
+
+    double positionasic_one = positionasic[chip][0];
+    double positionasic_two = positionasic[chip][1];
+    double positionasic_thr = positionasic[chip][2];
+
+    // Sectors Maps //
+    if ( ( aDigit->stave() > 1 ) || ( fabs( y ) > 2 ) ) {
+      ++m_2d_hitmap_sectors[aDigit->layer()][{ x + 0.25, y + 0.25 }];
+      ++m_2d_hitmap_sectors[aDigit->layer()][{ x + 0.25, y - 0.25 }];
+      ++m_2d_hitmap_sectors[aDigit->layer()][{ x - 0.25, y + 0.25 }];
+      ++m_2d_hitmap_sectors[aDigit->layer()][{ x - 0.25, y - 0.25 }];
+    }
+    if ( ( ( aDigit->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aDigit->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) {
+      ++m_2d_hitmap_sectors[aDigit->layer()][{ x, y + 0.25 }];
+      ++m_2d_hitmap_sectors[aDigit->layer()][{ x, y - 0.25 }];
+    }
+    if ( ( aDigit->stave() == 0 ) && ( fabs( y ) < 1 ) ) { ++m_2d_hitmap_sectors[aDigit->layer()][{ x, y }]; }
+
+    // Chips Maps //
+    if ( ( aDigit->stave() > 1 ) || ( fabs( y ) > 2 ) ) {
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_one, y + 0.25 }];
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_one, y - 0.25 }];
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_two, y + 0.25 }];
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_two, y - 0.25 }];
+    }
+    if ( ( ( aDigit->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aDigit->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) {
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }];
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }];
+    }
+    if ( ( aDigit->stave() == 0 ) && ( fabs( y ) < 1 ) ) {
+      ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_thr, y }];
+    }
+  }
+}; // End of class UTDigitsMonitor
+DECLARE_COMPONENT( UTDigitsMonitor )
diff --git a/UT/UTMonitors/src/UTErrorMonitor.cpp b/UT/UTMonitors/src/UTErrorMonitor.cpp
new file mode 100644
index 00000000000..cd315fd0c04
--- /dev/null
+++ b/UT/UTMonitors/src/UTErrorMonitor.cpp
@@ -0,0 +1,351 @@
+/*****************************************************************************\
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+#include "Event/UTErrorASIC.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "LHCbAlgs/Consumer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+#include "UTDet/DeUTDetector.h"
+#include <Gaudi/Accumulators/Histogram.h>
+#include <Gaudi/Accumulators/HistogramArray.h>
+#include <Kernel/UTDAQID.h>
+
+using namespace LHCb;
+
+/** @class UTErrorMonitor UTErrorMonitor.h
+ *  --------------------------------------------------
+ *  Class for monitoring UTError and UTSpecial banks
+ *  @author Wojciech Krupa (wokrupa@cern.ch) based on code by:
+ *  @author C. Hadjivasiliou
+ *  @date   2024/03/20
+ *  --------------------------------------------------
+ */
+
+enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 }; /// Enumeration for use in bit packing
+
+// Definition of axis
+namespace GA                               = Gaudi::Accumulators;
+const GA::Axis<double> AxisADC             = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31
+const GA::Axis<double> AxisCalib           = GA::Axis<double>( 256, -0.5, 256.5, "CalibStep" );
+const GA::Axis<double> AxisBCID            = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" );
+const GA::Axis<double> AxisDIGIT           = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" );
+const GA::Axis<double> AxisXnaturalunits   = GA::Axis<double>( 36, -9, 9, "Staves (Sectors)" );
+const GA::Axis<double> AxisXnaturalunits4x = GA::Axis<double>( 144, -9, 9, "Staves (ASICs)" );
+const GA::Axis<double> AxisYnaturalunits   = GA::Axis<double>( 28, -7, 7, "Modules" );
+
+// Definition of histogram names
+const std::array<std::string, 4> s_HistBanksNames = { "UTA_BankType_vs_TELL40_Flow0", "UTA_BankType_vs_TELL40_Flow1",
+                                                      "UTC_BankType_vs_TELL40_Flow0", "UTC_BankType_vs_TELL40_Flow1" };
+
+const std::array<std::string, 4> s_HistBanksNames2 = {
+    "UTA_BankType_vs_TELL40_Error_Flow0", "UTA_BankType_vs_TELL40_Error_Flow1", "UTC_BankType_vs_TELL40_Error_Flow0",
+    "UTC_BankType_vs_TELL40_Error_Flow1" };
+
+// UTError Types
+const std::array<std::string, 12> s_SpecialSubtypeMap = {
+    "UTA_BusyEvent_vs_TELL40_Flow0",   "UTA_BusyEvent_vs_TELL40_Flow1",   "UTC_BusyEvent_vs_TELL40_Flow0",
+    "UTC_BusyEvent_vs_TELL40_Flow1",   "UTA_BufferFull_vs_TELL40_Flow0",  "UTA_BufferFull_vs_TELL40_Flow1",
+    "UTC_BufferFull_vs_TELL40_Flow0",  "UTC_BufferFull_vs_TELL40_Flow1",  "UTA_BufferFullN_vs_TELL40_Flow0",
+    "UTA_BufferFullN_vs_TELL40_Flow1", "UTC_BufferFullN_vs_TELL40_Flow0", "UTC_BufferFullN_vs_TELL40_Flow1" };
+
+// 2D plots
+const double positionasic[4][3] = {
+    { -0.4375, -0.3125, -0.1875 }, // Chip 0
+    { -0.1875, -0.0625, -0.0625 }, // Chip 1
+    { +0.1875, +0.0625, +0.0625 }, // Chip 2
+    { +0.4375, +0.3125, +0.1875 }  // Chip 3
+};
+using namespace LHCb;
+
+template <masks m>
+[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) {
+  constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) );
+  return ( i & static_cast<unsigned int>( m ) ) >> b;
+}
+
+class UTErrorMonitor
+    : public LHCb::Algorithm::Consumer<void( UTErrorASICs const&, DeUTDetector const& ),
+                                       LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> {
+
+public:
+  /// constructer
+  UTErrorMonitor( const std::string& name, ISvcLocator* svcloc )
+      : Consumer{
+            name,
+            svcloc,
+            { { "InputData", UTErrorASICLocation::UTErrorASICs }, { "UTLocation", DeUTDetLocation::location() } } } {}
+
+  ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" };
+
+  // UTCoordinations map
+  mutable UTCoordinatesMap UTMap;
+
+  // TELL40 vs banktype plots
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistBanksNames.size()>
+      m_UT_Tell_40_vs_banktype{ this,
+                                []( int i ) { return fmt::format( "{}", s_HistBanksNames[i] ); },
+                                []( int i ) { return fmt::format( "{}", s_HistBanksNames[i] ); },
+                                { 54, 0, 54 },
+                                { 19, 0, 19 } };
+
+  // TELL40 vs banktype plots
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistBanksNames2.size()>
+      m_UT_Tell_40_vs_banktype_error{ this,
+                                      []( int i ) { return fmt::format( "{}", s_HistBanksNames2[i] ); },
+                                      []( int i ) { return fmt::format( "{}", s_HistBanksNames2[i] ); },
+                                      { 54, 0, 54 },
+                                      { 3, 0, 3 } };
+
+  // TELL40 vs packetID (UTError) plots
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_SpecialSubtypeMap.size()>
+      m_2d_packetype{ this,
+                      []( int i ) { return fmt::format( "{}", s_SpecialSubtypeMap[i] ); },
+                      []( int i ) { return fmt::format( "{}", s_SpecialSubtypeMap[i] ); },
+                      { 54, 0, 54 },
+                      { 24, 0, 24 } };
+
+  // 2D plot for geometrical pos of UTErrorKnown
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_asics_UTError_Known{
+      this,
+      []( int i ) { return fmt::format( "UTError_Known_Layer{}", i ); },
+      []( int i ) { return fmt::format( "UTError_Known_Layer{}", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // Error type counters
+  mutable GA::Histogram<1> m_nErrorsTypeCounter{ this, "ErrorsTypeCounter", "ErrorsTypeCounter", { 19, 0, 19 } };
+  // Number of ASICs in error per event - subtype 19
+  mutable GA::Histogram<1> m_ASICs_nErrorsType19{
+      this, "Number_ASICs_Error_19", "Number of ASICs in Error - subtype 19", { 1001, -0.5, 1000.5 } };
+  // Number of ASICs in error per event - subtype 20
+  mutable GA::Histogram<1> m_ASICs_nErrorsType20{
+      this, "Number_ASICs_Error_20", "Number of ASICs in Error - subtype 20", { 1001, -0.5, 1000.5 } };
+  // Number of ASICs in error per event - subtype 21
+  mutable GA::Histogram<1> m_ASICs_nErrorsType21{
+      this, "Number_ASICs_Error_21", "Number of ASICs in Error - subtype 21", { 1001, -0.5, 1000.5 } };
+
+  /// initialize
+  StatusCode initialize() override {
+    return Consumer::initialize().andThen( [&] {
+      // Set the top directory to UT
+      if ( histoTopDir().empty() ) setHistoTopDir( "" );
+    } );
+  }
+
+  /// execute
+  void operator()( const UTErrorASICs& errorsCont, DeUTDetector const& det ) const override {
+    // number of errors
+    unsigned int asic_inError_19 = 0;
+    unsigned int asic_inError_20 = 0;
+    unsigned int asic_inError_21 = 0;
+
+    plot( (double)errorsCont.size(), "nErrors", -0.5, 9999.5, 10000 );
+    // histos per bank type
+    // histos per error
+    for ( const auto& e : errorsCont ) {
+
+      // Handling UTError enocded in strip 1
+      if ( e->strip() % 128 == 1 && e->packetId() == 19 ) asic_inError_19++;
+      if ( e->strip() % 128 == 1 && e->packetId() == 20 ) asic_inError_20++;
+      if ( e->strip() % 128 == 1 && e->packetId() == 21 ) asic_inError_21++;
+
+      fillBankHistograms( e );
+      fillHeatMap_Error( e, det );
+    }
+
+    // Bank type counter
+    if ( asic_inError_19 > 1000 ) asic_inError_19 = 1000; // saturation
+    if ( asic_inError_20 > 1000 ) asic_inError_20 = 1000; // saturation
+    if ( asic_inError_21 > 1000 ) asic_inError_21 = 1000; // saturation
+
+    ++m_ASICs_nErrorsType19[asic_inError_19];
+    ++m_ASICs_nErrorsType20[asic_inError_20];
+    ++m_ASICs_nErrorsType21[asic_inError_21];
+  }
+
+private:
+  void fillBankHistograms( const LHCb::UTErrorASIC* aError ) const {
+    // Channel and source ID
+    auto         chanID          = aError->channelID();
+    UTDAQID      daqID           = readoutTool->channelIDToDAQID( chanID );
+    auto         board_ID        = daqID.board();
+    unsigned int sourceID        = UTMap.getSourceIDfromBoardNumber( board_ID );
+    int          y               = aError->bankType();
+    auto         LaneID          = extract<masks::lane>( aError->getdaqID() );
+    int          SourceID_       = sourceID / 1000;
+    unsigned int asic_times_lane = aError->asic() + 4 * LaneID;
+
+    // Bank type counter
+    ++m_nErrorsTypeCounter[y];
+
+    // Sadly we (I) can't do it better. But at least its ok!
+    // Normal ErrorType
+    if ( ( aError->strip() == 0 && LaneID == 0 ) ||
+         ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" && aError->strip() == 256 && LaneID == 0 ) ) {
+      // TELL40 vs Bank Type
+      if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) {
+        if ( sourceID / 1000 == 10 ) // 10346-10269
+          ++m_UT_Tell_40_vs_banktype[0][{ UTMap.getTELL40Bin( sourceID ), y }];
+        else if ( sourceID / 1000 == 11 ) // 11370-11293
+          ++m_UT_Tell_40_vs_banktype[1][{ UTMap.getTELL40Bin( sourceID ), y }];
+      } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) {
+        if ( sourceID / 1000 == 12 ) // 12366-12291
+          ++m_UT_Tell_40_vs_banktype[2][{ UTMap.getTELL40Bin( sourceID ), y }];
+        else if ( sourceID / 1000 == 13 ) // 13371-13315
+          ++m_UT_Tell_40_vs_banktype[3][{ UTMap.getTELL40Bin( sourceID ), y }];
+      }
+    }
+
+    if ( aError->strip() % 128 == 1 &&
+         ( aError->packetId() == 19 || aError->packetId() == 20 || aError->packetId() == 21 ) ) {
+
+      // TELL40 vs Bank Type
+      if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) {
+        if ( sourceID / 1000 == 10 ) // 10346-10269
+          ++m_UT_Tell_40_vs_banktype_error[0][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }];
+        else if ( sourceID / 1000 == 11 ) // 11370-11293
+          ++m_UT_Tell_40_vs_banktype_error[1][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }];
+      } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) {
+        if ( sourceID / 1000 == 12 ) // 12366-12291
+          ++m_UT_Tell_40_vs_banktype_error[2][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }];
+        else if ( sourceID / 1000 == 13 ) // 13371-13315
+          ++m_UT_Tell_40_vs_banktype_error[3][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }];
+      }
+
+      // Suberror plots
+      if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) {
+        if ( SourceID_ == 10 ) {
+          if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4]
+                            [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+          } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) {
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4]
+                            [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }];
+          } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                      UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                      UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) {
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+          }
+        } else if ( SourceID_ == 11 ) {
+          if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+            if ( asic_times_lane == 16 || asic_times_lane == 17 )
+              ++m_2d_packetype[( aError->packetId() - 19 ) * 4]
+                              [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+            else
+              ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 1]
+                              [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }];
+          } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" )
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 1]
+                            [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }];
+          else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" )
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 1][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+        }
+
+      } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) {
+        if ( SourceID_ == 12 ) {
+          if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 2]
+                            [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+          } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) {
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + +2]
+                            [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }];
+          } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                      UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                      UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) {
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 2][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+          }
+        }
+
+        else if ( SourceID_ == 13 ) {
+          if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) {
+            if ( asic_times_lane == 16 || asic_times_lane == 17 )
+              ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 2]
+                              [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+            else
+              ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 3]
+                              [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }];
+          } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" )
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 3]
+                            [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }];
+          else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" ||
+                    UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" )
+            ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 3][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }];
+        }
+      }
+    }
+  }
+
+  void incrementHistogram( GA::Histogram<2>& histogram, double x, double y, double positionasic_one,
+                           double positionasic_two ) const {
+    ++histogram[{ x + positionasic_one, y + 0.25 }];
+    ++histogram[{ x + positionasic_two, y + 0.25 }];
+    ++histogram[{ x + positionasic_one, y - 0.25 }];
+    ++histogram[{ x + positionasic_two, y - 0.25 }];
+  }
+
+  void fillHeatMap_Error( const LHCb::UTErrorASIC* aError, DeUTDetector const& det ) const {
+
+    // Let's find out where we are in the UT, it should be reoptimised in the future
+    // To be checked if it works properly!
+
+    if ( aError->strip() % 128 == 1 ) {
+      std::tuple<float, float, float, float, std::string, std::string> tuple =
+          UTMap.getTuple( aError->module(), aError->face(), aError->stave(), aError->side(), aError->sector() );
+
+      float       x           = std::get<0>( tuple );
+      float       y           = std::get<1>( tuple );
+      std::string module_name = std::get<4>( tuple );
+      std::string type        = std::get<5>( tuple );
+      auto        chanID      = aError->channelID();
+      auto        aSector     = det.findSector( chanID );
+#ifdef USE_DD4HEP
+      auto trajStrip0   = aSector.createTraj( 0, 0 );
+      auto trajStrip511 = aSector.createTraj( 511, 0 );
+#else
+      auto trajStrip0   = aSector->trajectory( chanID, 0 );
+      auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ chanID + 511 }, 0 );
+#endif
+      double stripx0    = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5;
+      double stripx511  = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5;
+      double stripwidth = stripx511 - stripx0;
+      int    chip;
+
+      if ( stripwidth > 0 )
+        chip = int( aError->strip() / 128 );
+      else
+        chip = 3 - int( aError->strip() / 128 );
+
+      double positionasic_one = positionasic[chip][0];
+      double positionasic_two = positionasic[chip][1];
+      double positionasic_thr = positionasic[chip][2];
+
+      // Chip is in its correct geometrical order
+      // Sector map //
+      if ( ( aError->stave() > 1 ) || ( fabs( y ) > 2 ) ) {
+        assert( aError->layer() >= 0 && aError->layer() <= 3 );
+        incrementHistogram( m_asics_UTError_Known[aError->layer()], x, y, positionasic_one, positionasic_two );
+      }
+
+      if ( ( ( aError->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aError->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) {
+        ++m_asics_UTError_Known[aError->layer() == 0][{ x + positionasic_thr, y + 0.25 }];
+        ++m_asics_UTError_Known[aError->layer() == 0][{ x + positionasic_thr, y - 0.25 }];
+      }
+      if ( ( aError->stave() == 0 ) && ( fabs( y ) < 1 ) ) {
+        ++m_asics_UTError_Known[aError->layer()][{ x + positionasic_thr, y }];
+      }
+    }
+  }
+};
+
+DECLARE_COMPONENT( UTErrorMonitor )
diff --git a/UT/UTMonitors/src/UTNZSMonitor.cpp b/UT/UTMonitors/src/UTNZSMonitor.cpp
new file mode 100644
index 00000000000..c24ffae4793
--- /dev/null
+++ b/UT/UTMonitors/src/UTNZSMonitor.cpp
@@ -0,0 +1,372 @@
+/***************************************************************************** \
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the teRMS_ADC of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/UTDigit.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "Kernel/UTDAQBoard.h"
+#include "Kernel/UTDAQDefinitions.h"
+#include "Kernel/UTDAQID.h"
+#include "LHCbAlgs/Consumer.h"
+#include "LHCbAlgs/Transformer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+#include "UTDAQ/UTDAQHelper.h"
+#include "UTDAQ/UTInfo.h"
+#include "UTDet/DeUTDetector.h"
+#include <Event/ODIN.h>
+#include <Event/RawBank.h>
+#include <Event/RawEvent.h>
+#include <Gaudi/Accumulators/Histogram.h>
+#include <Gaudi/Accumulators/HistogramArray.h>
+#include <Kernel/IUTReadoutTool.h>
+#include <algorithm>
+#include <mutex>
+#include <optional>
+#include <string>
+
+/** @class UTNZSMonitor UTNZSMonitor.cpp
+ * --------------------------------------------------
+ *  Class for monitoring UTNZS banks
+ *    @author Wojciech Krupa (wokrupa@cern.ch):
+ *    @date   2024/02/10
+ * --------------------------------------------------
+ */
+using namespace LHCb;
+namespace GA = Gaudi::Accumulators;
+
+// Definition of axis
+const GA::Axis<double> AxisADC             = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31
+const GA::Axis<double> AxisCalib           = GA::Axis<double>( 257, -0.5, 256.5, "CalibStep" );
+const GA::Axis<double> AxisBCID            = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" );
+const GA::Axis<double> AxisDIGIT           = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" );
+const GA::Axis<double> AxisXnaturalunits   = GA::Axis<double>( 36, -9, 9, "Staves (Sectors)" );
+const GA::Axis<double> AxisXnaturalunits4x = GA::Axis<double>( 144, -9, 9, "Staves (ASICs)" );
+const GA::Axis<double> AxisYnaturalunits   = GA::Axis<double>( 28, -7, 7, "Modules" );
+const GA::Axis<double> AxisModule          = GA::Axis<double>( 512, 0, 512, "Strips" );
+
+enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 };
+
+const std::array<std::string, 4> s_HistNames = { "UTA_Tell_40_vs_ASIC_Flow0", "UTA_Tell_40_vs_ASIC_Flow1",
+                                                 "UTC_Tell_40_vs_ASIC_Flow0", "UTC_Tell_40_vs_ASIC_Flow1" };
+
+const std::array<std::string, 4> s_HistBanksNames = { "UTA_BankType_vs_TELL40_Flow0", "UTA_BankType_vs_TELL40_Flow1",
+                                                      "UTC_BankType_vs_TELL40_Flow0", "UTC_BankType_vs_TELL40_Flow1" };
+
+// UT layers
+const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" };
+
+// 2D plots
+const double positionasic[4][3] = {
+    { -0.4375, -0.3125, -0.1875 }, // Chip 0
+    { -0.1875, -0.0625, -0.0625 }, // Chip 1
+    { +0.1875, +0.0625, +0.0625 }, // Chip 2
+    { +0.4375, +0.3125, +0.1875 }  // Chip 3
+};
+
+template <masks m>
+[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) {
+  constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) );
+  return ( i & static_cast<unsigned int>( m ) ) >> b;
+}
+
+// Tools for booking histograms
+namespace Utility {
+  template <typename T, typename OWNER>
+  void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1 ) {
+    t.emplace( std::piecewise_construct, std::forward_as_tuple( key ),
+               std::forward_as_tuple( owner, key, title, axis1 ) );
+  }
+  template <typename T, typename OWNER>
+  void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis2 ) {
+    t.emplace( std::piecewise_construct, std::forward_as_tuple( key ),
+               std::forward_as_tuple( owner, key, title, axis1, axis2 ) );
+  }
+
+} // namespace Utility
+
+/** ------------------------------------------------------------------------------------------------------------------------ */
+class UTNZSMonitor
+    : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, DeUTDetector const&, LHCb::ODIN const& ),
+                                       LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> {
+public:
+  UTNZSMonitor( const std::string& name, ISvcLocator* svcloc )
+      : Consumer{ name,
+                  svcloc,
+                  { { "InputData", UTDigitLocation::UTDigits },
+                    { "InputErrData", UTDigitLocation::UTDigits },
+                    { "UTLocation", DeUTDetLocation::location() },
+                    { "ODINLocation", LHCb::ODINLocation::Default } } } {}
+  ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" };
+
+  StatusCode initialize() override;
+  void       operator()( const UTDigits&, const UTDigits&, DeUTDetector const&, LHCb::ODIN const& ) const override;
+  void       fillADC( const LHCb::UTDigit*, LHCb::ODIN const& ) const;
+  void       fillFlow( const LHCb::UTDigit* ) const;
+  void       incrementHistogram( GA::ProfileHistogram<2>&, double, double, double, double, float ) const;
+  void       fillHeatMap_naturalunits( const LHCb::UTDigit*, DeUTDetector const& ) const;
+
+private:
+  mutable UTCoordinatesMap UTMap;
+
+  // TELL40 plots
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistNames.size()>
+      m_UT_Tell_40_vs_asic{ this,
+                            []( int i ) { return fmt::format( "{}", s_HistNames[i] ); },
+                            []( int i ) { return fmt::format( "{}", s_HistNames[i] ); },
+                            { 54, 0, 54 },
+                            { 24, 0, 24 } };
+
+  // The container for Mean_ADC per channel
+  mutable std::map<std::string, GA::ProfileHistogram<1>> m_2d_Mean_ADCCounter;
+  mutable std::map<std::string, GA::ProfileHistogram<1>> m_2d_RMS_ADCCounter;
+  mutable std::map<std::string, GA::Histogram<2>>        m_2d_ADCCounter;
+
+  // 2D plots with MCMS value per chip
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_UT_mcm_strip{
+      this,
+      []( int i ) { return fmt::format( "MCM_strip_Layer{}", i ); },
+      []( int i ) { return fmt::format( "MCM_strip_Layer{} (ASICs)", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // 2D plots with MCMS strip per chip
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_UT_mcm_mu{
+      this,
+      []( int i ) { return fmt::format( "MCM_mu_Layer{}", i ); },
+      []( int i ) { return fmt::format( "MCM_mu_Layer{} (ASICs)", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // 2D plots with MCMS strip per chip
+  mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_UT_MCM_rms_ADC{
+      this,
+      []( int i ) { return fmt::format( "MCM_rms_Layer{}", i ); },
+      []( int i ) { return fmt::format( "MCM_rms_Layer{} (ASICs)", i ); },
+      { 144, -9, 9, "Staves (ASICs)" },
+      { 28, -7, 7, "Modules" } };
+
+  // 1D plots: ADC
+  mutable GA::Histogram<2> m_nADCvsBCID{ this, "ADCvsBuncId", "ADC vs BuncId", AxisBCID, AxisADC };
+  mutable GA::Histogram<2> m_nADCvsCalibStep{ this, "ADCvsCalibStep", "ADC vs CalibStep", AxisCalib, AxisADC };
+
+  mutable std::map<std::string, Gaudi::Accumulators::SigmaCounter<>> RawADC;
+  mutable std::map<std::string, Gaudi::Accumulators::SigmaCounter<>> mcm;
+
+}; // End of class UTNZSMonitor
+DECLARE_COMPONENT( UTNZSMonitor )
+
+StatusCode UTNZSMonitor::initialize() {
+  return Consumer::initialize().andThen( [&] {
+    // Set the top directory to UT
+    if ( histoTopDir().empty() ) setHistoTopDir( "" );
+
+    for ( const auto& module : UTMap.getModulesNames() ) {
+      Utility::map_emplace( m_2d_Mean_ADCCounter, "Mean_ADC_" + module, this, "Mean_ADC_" + module, { 512, 0, 512 } );
+      Utility::map_emplace( m_2d_RMS_ADCCounter, "RMS_ADC_" + module, this, "RMS_ADC_" + module, { 512, 0, 512 } );
+      Utility::map_emplace( m_2d_ADCCounter, "ADC_" + module, this, "ADC_" + module, { 512, 0, 512 },
+                            { 64, -32.5, 31.5 } );
+    }
+  } );
+}
+
+void UTNZSMonitor::operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont, DeUTDetector const& det,
+                               LHCb::ODIN const& odin ) const {
+
+  // General counters
+  // -------------------------------------------------------
+  // Loop on digits
+  // side==1 is Aside (left), side==0 is Cside (right)
+  // -------------------------------------------------------
+  for ( const UTDigit* d : digitsCont ) {
+    fillFlow( d );
+    fillADC( d, odin );
+    fillHeatMap_naturalunits( d, det );
+  }
+
+  for ( const UTDigit* d : errdigitsCont ) {
+    fillFlow( d );
+    fillADC( d, odin );
+    fillHeatMap_naturalunits( d, det );
+  }
+  // -------------------------------------------------------
+  // end loop on digits
+  // -------------------------------------------------------
+} // end execute
+
+void UTNZSMonitor::fillADC( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin ) const {
+
+  // Summary plots
+  ++m_nADCvsBCID[{ odin.bunchId(), aDigit->depositedCharge() }];
+  ++m_nADCvsCalibStep[{ odin.calibrationStep(), aDigit->depositedCharge() }];
+
+  // ADC counter
+  auto tuple = UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+  std::string module_name = std::get<4>( tuple );
+
+  RawADC[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( aDigit->strip() )] +=
+      aDigit->depositedCharge();
+
+  m_2d_Mean_ADCCounter.at( "Mean_ADC_" + UT_layers[aDigit->layer()] + "_" + module_name )[aDigit->strip()] +=
+      RawADC[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( aDigit->strip() )].mean();
+  m_2d_RMS_ADCCounter.at( "RMS_ADC_" + UT_layers[aDigit->layer()] + "_" + module_name )[aDigit->strip()] +=
+      RawADC[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( aDigit->strip() )]
+          .standard_deviation();
+  ++m_2d_ADCCounter.at( "ADC_" + UT_layers[aDigit->layer()] + "_" +
+                        module_name )[{ aDigit->strip(), aDigit->depositedCharge() }];
+}
+
+void UTNZSMonitor::fillFlow( const LHCb::UTDigit* aDigit ) const {
+  Detector::UT::ChannelID channelID = aDigit->channelID();
+  UTDAQID                 daqID     = readoutTool->channelIDToDAQID( channelID );
+  unsigned int            board_ID  = daqID.board();
+  unsigned int            sourceID  = UTMap.getSourceIDfromBoardNumber( board_ID );
+  int                     SourceID_ = sourceID / 1000;
+
+  unsigned int asic_times_lane = aDigit->asic() % 4 + 4 * extract<masks::lane>( aDigit->getdaqID() );
+
+  // Get TELL40 and Flavour only once
+  std::string tell40    = UTMap.getTELL40( sourceID );
+  std::string flavour   = UTMap.getFlavour( tell40 );
+  int         tell40Bin = UTMap.getTELL40Bin( sourceID );
+
+  // Check if the strip is aligned for filling
+  if ( aDigit->strip() % 128 == 0 ) {
+    if ( tell40.find( "UA" ) != std::string::npos ) {
+      // Handle UA sourceID_ == 10 and 11
+      if ( SourceID_ == 10 ) {
+        if ( flavour == "2x4" ) {
+          ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+        } else if ( flavour == "2x5" ) {
+          ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, UTMap.get2_5_Flow0_Bin( asic_times_lane ) }];
+        } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) {
+          ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, asic_times_lane }];
+        }
+      } else if ( SourceID_ == 11 ) {
+        if ( flavour == "2x4" ) {
+          if ( asic_times_lane == 16 || asic_times_lane == 17 ) {
+            ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+          } else {
+            ++m_UT_Tell_40_vs_asic[1][{ tell40Bin, UTMap.get2_4_Flow1_Bin( asic_times_lane ) }];
+          }
+        } else if ( flavour == "2x5" ) {
+          ++m_UT_Tell_40_vs_asic[1][{ tell40Bin, UTMap.get2_5_Flow1_Bin( asic_times_lane ) }];
+        } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) {
+          ++m_UT_Tell_40_vs_asic[1][{ tell40Bin, asic_times_lane }];
+        }
+      }
+    } else if ( tell40.find( "UC" ) != std::string::npos ) {
+      // Handle UC sourceID_ == 12 and 13
+      if ( SourceID_ == 12 ) {
+        if ( flavour == "2x4" ) {
+          ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+        } else if ( flavour == "2x5" ) {
+          ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, UTMap.get2_5_Flow0_Bin( asic_times_lane ) }];
+        } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) {
+          ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, asic_times_lane }];
+        }
+      } else if ( SourceID_ == 13 ) {
+        if ( flavour == "2x4" ) {
+          if ( asic_times_lane == 16 || asic_times_lane == 17 ) {
+            ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }];
+          } else {
+            ++m_UT_Tell_40_vs_asic[3][{ tell40Bin, UTMap.get2_4_Flow1_Bin( asic_times_lane ) }];
+          }
+        } else if ( flavour == "2x5" ) {
+          ++m_UT_Tell_40_vs_asic[3][{ tell40Bin, UTMap.get2_5_Flow1_Bin( asic_times_lane ) }];
+        } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) {
+          ++m_UT_Tell_40_vs_asic[3][{ tell40Bin, asic_times_lane }];
+        }
+      }
+    }
+  }
+}
+
+void UTNZSMonitor::incrementHistogram( GA::ProfileHistogram<2>& histogram, double x, double y, double positionasic_one,
+                                       double positionasic_two, float val ) const {
+
+  histogram[{ x + positionasic_one, y + 0.25 }] += val;
+  histogram[{ x + positionasic_two, y + 0.25 }] += val;
+  histogram[{ x + positionasic_one, y - 0.25 }] += val;
+  histogram[{ x + positionasic_two, y - 0.25 }] += val;
+}
+
+void UTNZSMonitor::fillHeatMap_naturalunits( const LHCb::UTDigit* aDigit, DeUTDetector const& det ) const {
+
+  if ( aDigit->strip() % 128 == 0 ) {
+
+    std::tuple<float, float, float, float, std::string, std::string> tuple =
+        UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+
+    // Let's find out where we are
+    float       x           = std::get<0>( tuple );
+    float       y           = std::get<1>( tuple );
+    std::string module_name = std::get<4>( tuple );
+    std::string type        = std::get<5>( tuple );
+    auto        chanID      = aDigit->channelID();
+    auto        aSector     = det.findSector( chanID );
+#ifdef USE_DD4HEP
+    auto trajStrip0   = aSector.createTraj( 0, 0 );
+    auto trajStrip511 = aSector.createTraj( 511, 0 );
+#else
+    auto trajStrip0   = aSector->trajectory( chanID, 0 );
+    auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ chanID + 511 }, 0 );
+#endif
+    double stripx0    = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5;
+    double stripx511  = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5;
+    double stripwidth = stripx511 - stripx0;
+    int    chip;
+
+    if ( stripwidth > 0 )
+      chip = int( aDigit->strip() / 128 );
+    else
+      chip = 3 - int( aDigit->strip() / 128 );
+
+    double positionasic_one = positionasic[chip][0];
+    double positionasic_two = positionasic[chip][1];
+    double positionasic_thr = positionasic[chip][2];
+
+    float mcm_val = aDigit->mcmVal() + 0.0001; // protection against 0 value in bin which caused problem in displaying
+                                               // histogram sometimes even bigger statistic the average is 0. Monet
+                                               // interpret it as empty bin what is not true.  won't process more
+                                               // than O(4) NMZS events per run so it wont be a problem
+
+    mcm[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( chip )] += mcm_val;
+    float mcm_mu = mcm[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( chip )].mean() + 0.0001;
+    float MCM_rms_ADC =
+        mcm[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( chip )].standard_deviation() + 0.001;
+
+    // Sector map //
+    if ( ( aDigit->stave() > 1 ) || ( fabs( y ) > 2 ) ) {
+      incrementHistogram( m_UT_mcm_strip[aDigit->layer()], x, y, positionasic_one, positionasic_two,
+                          aDigit->mcmStrip() );
+      incrementHistogram( m_UT_mcm_mu[aDigit->layer()], x, y, positionasic_one, positionasic_two, mcm_mu );
+      incrementHistogram( m_UT_MCM_rms_ADC[aDigit->layer()], x, y, positionasic_one, positionasic_two, MCM_rms_ADC );
+    }
+
+    if ( ( ( aDigit->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aDigit->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) {
+      m_UT_mcm_strip[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }] += aDigit->mcmStrip();
+      m_UT_mcm_strip[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }] += aDigit->mcmStrip();
+      m_UT_mcm_mu[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }] += mcm_mu;
+      m_UT_mcm_mu[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }] += mcm_mu;
+      m_UT_MCM_rms_ADC[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }] += MCM_rms_ADC;
+      m_UT_MCM_rms_ADC[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }] += MCM_rms_ADC;
+    }
+
+    if ( ( aDigit->stave() == 0 ) && ( fabs( y ) < 1 ) ) {
+      { m_UT_mcm_strip[aDigit->layer()][{ x + positionasic_thr, y }] += aDigit->mcmStrip(); }
+      { m_UT_mcm_mu[aDigit->layer()][{ x + positionasic_thr, y }] += mcm_mu; }
+      { m_UT_MCM_rms_ADC[aDigit->layer()][{ x + positionasic_thr, y }] += MCM_rms_ADC; }
+    }
+  }
+}
diff --git a/UT/UTMonitors/src/UTSuperTAEMonitor.cpp b/UT/UTMonitors/src/UTSuperTAEMonitor.cpp
new file mode 100644
index 00000000000..b22c76ed5fc
--- /dev/null
+++ b/UT/UTMonitors/src/UTSuperTAEMonitor.cpp
@@ -0,0 +1,179 @@
+/*****************************************************************************\
+* (c) Copyright 2020 CERN for the benefit of the LHCb Collaboration           *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/ODIN.h"
+#include "Event/TAEUtils.h"
+#include "Event/UTDigit.h"
+#include "Gaudi/Accumulators/Histogram.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "GaudiKernel/GaudiException.h"
+#include "GaudiKernel/PhysicalConstants.h"
+#include "GaudiKernel/SystemOfUnits.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "Kernel/STLExtensions.h"
+#include "Kernel/UTDAQID.h"
+#include "LHCbAlgs/Consumer.h"
+#include "LHCbAlgs/MergingTransformer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+#include "UTDet/DeUTDetector.h"
+
+/** @class UTSuperTAEMonitor UTSuperTAEMonitor.cpp
+ * --------------------------------------------------
+ *  Counters for additional monitoring of UT (UT in global)
+ *  The algorithm produces several useful histograms
+ *  @author Wojciech Krupa
+ *  @date   2024/10/07
+ * --------------------------------------------------
+ **/
+
+using ODIN        = LHCb::ODIN;
+using ODINVector  = Gaudi::Functional::vector_of_const_<ODIN const*>;
+using InputVector = Gaudi::Functional::vector_of_const_<LHCb::UTDigits const*>;
+using BXTypes     = LHCb::ODIN::BXTypes;
+using EventTypes  = LHCb::ODIN::EventTypes;
+using namespace LHCb;
+namespace GA = Gaudi::Accumulators;
+
+// Tools for booking histograms
+namespace Utility {
+  template <typename T, typename OWNER>
+  void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis2 ) {
+    t.emplace( std::piecewise_construct, std::forward_as_tuple( key ),
+               std::forward_as_tuple( owner, key, title, axis1, axis2 ) );
+  }
+} // namespace Utility
+
+class UTSuperTAEMonitor final : public LHCb::Algorithm::MergingConsumer<void( ODINVector const&, InputVector const& )> {
+
+public:
+  UTSuperTAEMonitor( const std::string&, ISvcLocator* );
+  StatusCode initialize() override;
+  void       operator()( ODINVector const&, InputVector const& ) const override;
+  void       fillHistograms( const LHCb::UTDigit*, LHCb::ODIN const&, int ) const;
+
+private:
+  LHCb::TAE::Handler          m_taeHandler{ this };
+  mutable UTCoordinatesMap    UTMap;
+  Gaudi::Property<signed int> m_HalfWindow{ this, "HalfWindow", 3 };
+
+  using Histogram1D        = GA::Histogram<1, GA::atomicity::full, float>;
+  using Histogram2D        = GA::Histogram<2, GA::atomicity::full, float>;
+  using ProfileHistogram   = GA::ProfileHistogram<1, GA::atomicity::full, float>;
+  using ProfileHistogram2D = GA::ProfileHistogram<2, GA::atomicity::full, float>;
+  using Axis               = GA::Axis<float>;
+
+  // Definition of axis
+  const GA::Axis<double> AxisADC        = GA::Axis<double>( 64, -32.5, 31.5, "nADC" );       //-32-31
+  const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" );        // 0-31
+  const GA::Axis<double> AxisBCID       = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); //
+  const GA::Axis<double> AxisDIGIT      = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" );
+  const GA::Axis<double> AxisTAE =
+      GA::Axis<double>( 2 * m_HalfWindow + 1, -1 * m_HalfWindow - 0.5, m_HalfWindow + 0.5, "TAE index" );
+
+  const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" };
+
+  // General TAE counters
+  mutable Gaudi::Accumulators::Histogram<1> m_bxtype{ this, "BXType", "BXType", { 4, -0.5, 3.5 } };
+  mutable Gaudi::Accumulators::Histogram<1> m_trgtype{ this, "TrgType", "TrgType", { 16, -0.5, 15.5 } };
+  mutable Gaudi::Accumulators::Histogram<2> m_bcid_bxtype{
+      this, "BXTypeVsBXID", "BXTypeVsBXID", AxisBCID, { 4, -0.5, 3.5 } };
+  mutable Gaudi::Accumulators::Histogram<2> m_bcid_trgtype{
+      this, "TrgTypeVsBXID", "TrgTypeVsBXID", AxisBCID, { 16, -0.5, 15.5 } };
+  mutable Gaudi::Accumulators::WeightedHistogram<2> m_bcid_taesummary{
+      this, "TAESummary", "TAE summary", AxisBCID, { 14, -0.5, -0.5 + 14 } };
+  mutable Gaudi::Accumulators::Histogram<2> m_bcid_taeindex{
+      this, "TAEIndex", "TAE index", AxisBCID, { 64, -0.5, -0.5 + 64 } };
+
+  // Counters TAE step vs ADC per DCB or per quadrant or UT
+  mutable std::map<std::string, GA::Histogram<2>> m_2d_DCB_tae_adc;
+  mutable std::map<std::string, GA::Histogram<2>> m_2d_DCBQuadrant_tae_adc;
+  mutable Gaudi::Accumulators::Histogram<2>       m_2d_UT_tae_adc{ this, "UTTAE", "UTTAE", AxisTAE, AxisADC_signed };
+};
+
+// Declaration of the Algorithm Factory
+DECLARE_COMPONENT( UTSuperTAEMonitor )
+
+UTSuperTAEMonitor::UTSuperTAEMonitor( const std::string& name, ISvcLocator* pSvcLocator )
+    : LHCb::Algorithm::MergingConsumer<void( ODINVector const&, InputVector const& )>(
+          name, pSvcLocator, { KeyValues{ "ODINVector", {} }, KeyValues{ "InputVector", {} } } ){};
+
+StatusCode UTSuperTAEMonitor::initialize() {
+  return base_class::initialize().andThen( [&] {
+    // Set the top directory to UT
+    // Initialise counters
+    for ( const auto& DCB : UTMap.getDCBsAside() ) {
+      Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed );
+    }
+    for ( const auto& DCB : UTMap.getDCBsCside() ) {
+      Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed );
+    }
+    for ( const auto& DCBQuadrant : UTMap.getDCBQuadrants() ) {
+      Utility::map_emplace( m_2d_DCBQuadrant_tae_adc, DCBQuadrant, this, DCBQuadrant, AxisTAE, AxisADC_signed );
+    }
+  } );
+}
+
+void UTSuperTAEMonitor::operator()( ODINVector const& odinVector, InputVector const& inputVector ) const {
+
+  auto taeEvents = m_taeHandler.arrangeTAE( odinVector, inputVector, 3 );
+
+  if ( taeEvents.empty() ) { return; }
+
+  for ( auto it = taeEvents.begin(); it != taeEvents.end(); ++it ) {
+    int                   offset     = it->first; // 0 is central, negative for Prev, positive for Next
+    const LHCb::ODIN&     odin       = it->second.first;
+    const LHCb::UTDigits& digitsCont = it->second.second;
+
+    for ( auto d_it = digitsCont.begin(); d_it != digitsCont.end(); ++d_it ) { fillHistograms( *d_it, odin, offset ); }
+  }
+}
+
+void UTSuperTAEMonitor::fillHistograms( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin, int offset ) const {
+
+  // Let's find where we are in UT
+  auto tuple = UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+  std::string                module_name = std::get<4>( tuple );
+  auto                       y_p         = std::get<1>( tuple );
+  std::string                position    = ( y_p > 0 ) ? "T" : "B";
+  std::string                side        = ( aDigit->side() == 0 ) ? "C" : "A";
+  std::array<std::string, 4> layer       = { "UTaX", "UTaU", "UTbV", "UTbX" };
+  std::string                station     = ( aDigit->layer() < 2 ) ? "a" : "b";
+  std::string                dcb_name    = UTMap.getDCB( layer[aDigit->layer()] + "_" + module_name );
+  int                        TAEindex    = offset; //
+
+  // Let's find where we are in the LHCb
+  auto bcid    = odin.bunchId();
+  auto bx      = static_cast<uint8_t>( odin.bunchCrossingType() );
+  auto trgtype = odin.triggerType();
+
+  // simple counters
+  ++m_bxtype[bx];
+  ++m_trgtype[trgtype];
+  ++m_bcid_bxtype[{ bcid, bx }];
+  ++m_bcid_trgtype[{ bcid, trgtype }];
+
+  // summary plots this should be replaced by odin.eventType soon as below
+  double weight  = 1;
+  int    offset2 = 0;
+  m_bcid_taesummary[{ bcid, offset2 + bx }] += weight;
+  offset2 += 4;
+  m_bcid_taesummary[{ bcid, offset2++ }] += weight * odin.isTAE();
+  m_bcid_taesummary[{ bcid, offset2++ }] += weight * odin.timeAlignmentEventCentral();
+  m_bcid_taesummary[{ bcid, offset2++ }] += weight * odin.timeAlignmentEventFirst();
+
+  ++m_bcid_taeindex[{ bcid, odin.timeAlignmentEventIndex() }];
+  ++m_2d_UT_tae_adc[{ TAEindex, aDigit->depositedCharge() }];
+  ++m_2d_DCB_tae_adc.at( dcb_name )[{ TAEindex, aDigit->depositedCharge() }];
+  ++m_2d_DCBQuadrant_tae_adc.at(
+      UTMap.getDCBQuadrant( layer[aDigit->layer()] + "_" + module_name ) )[{ TAEindex, aDigit->depositedCharge() }];
+};
diff --git a/UT/UTMonitors/src/UTTAEMonitor.cpp b/UT/UTMonitors/src/UTTAEMonitor.cpp
new file mode 100644
index 00000000000..8d64c8f747f
--- /dev/null
+++ b/UT/UTMonitors/src/UTTAEMonitor.cpp
@@ -0,0 +1,165 @@
+/*****************************************************************************\
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/ODIN.h"
+#include "Event/UTDigit.h"
+#include "Gaudi/Accumulators/Histogram.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "Kernel/STLExtensions.h"
+#include "Kernel/UTDAQID.h"
+#include "LHCbAlgs/Consumer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+
+/** @class UTTAEMonitor UTTAEMonitor.h
+ * --------------------------------------------------
+ *  Class for monitoring TAE in Monet (UT in local)
+ *  @author W. Krupa
+ *  @author C. Hadjivasiliou
+ *  @date   2024/03/19
+ * --------------------------------------------------
+ **/
+
+namespace GA = Gaudi::Accumulators;
+using namespace LHCb;
+using EventTypes = LHCb::ODIN::EventTypes;
+
+// Tools for booking histograms
+namespace Utility {
+  template <typename T, typename OWNER>
+  void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1,
+                    Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis2 ) {
+    t.emplace( std::piecewise_construct, std::forward_as_tuple( key ),
+               std::forward_as_tuple( owner, key, title, axis1, axis2 ) );
+  }
+} // namespace Utility
+
+const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" };
+
+class UTTAEMonitor : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, LHCb::ODIN const& ),
+                                                      LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg>> {
+
+public:
+  UTTAEMonitor( const std::string&, ISvcLocator* );
+  StatusCode initialize() override;
+  void       operator()( const UTDigits&, const UTDigits&, LHCb::ODIN const& ) const override;
+  void       fillHistograms( const LHCb::UTDigit*, LHCb::ODIN const& ) const;
+
+  ToolHandle<IUTReadoutTool>  readoutTool{ this, "ReadoutTool", "UTReadoutTool" };
+  mutable UTCoordinatesMap    UTMap;
+  Gaudi::Property<signed int> m_HalfWindow{ this, "HalfWindow", 3 };
+
+  // Definition of axis
+  const GA::Axis<double> AxisADC        = GA::Axis<double>( 64, -32.5, 31.5, "nADC" );       //-32-31
+  const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" );        // 0-31
+  const GA::Axis<double> AxisBCID       = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); //
+  const GA::Axis<double> AxisDIGIT      = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" );
+  const GA::Axis<double> AxisTAE = GA::Axis<double>( 2 * m_HalfWindow + 1, -1 * m_HalfWindow - 0.5, m_HalfWindow + 0.5,
+                                                     "TAE index" ); // for windows 3 - central is 4
+private:
+  // General counters
+  mutable Gaudi::Accumulators::Histogram<1> m_bxtype{ this, "BXType", "BXType", { 4, -0.5, 3.5 } };
+  mutable Gaudi::Accumulators::Histogram<1> m_trgtype{ this, "TrgType", "TrgType", { 16, -0.5, 15.5 } };
+  mutable Gaudi::Accumulators::Histogram<2> m_bcid_bxtype{
+      this, "BXTypeVsBXID", "BXTypeVsBXID", AxisBCID, { 4, -0.5, 3.5 } };
+  mutable Gaudi::Accumulators::Histogram<2> m_bcid_trgtype{
+      this, "TrgTypeVsBXID", "TrgTypeVsBXID", AxisBCID, { 16, -0.5, 15.5 } };
+  mutable Gaudi::Accumulators::WeightedHistogram<2> m_bcid_taesummary{
+      this, "TAESummary", "TAE summary", AxisBCID, { 14, -0.5, -0.5 + 14 } };
+  mutable Gaudi::Accumulators::Histogram<2> m_bcid_taeindex{
+      this, "TAEIndex", "TAE index", AxisBCID, { 64, -0.5, -0.5 + 64 } };
+
+  // Counters TAE step vs ADC per DCB or per quadrant or UT
+  mutable std::map<std::string, GA::Histogram<2>> m_2d_DCB_tae_adc;
+  mutable std::map<std::string, GA::Histogram<2>> m_2d_DCBQuadrant_tae_adc;
+  mutable Gaudi::Accumulators::Histogram<2>       m_2d_UT_tae_adc{ this, "UTTAE", "UTTAE", AxisTAE, AxisADC_signed };
+};
+DECLARE_COMPONENT( UTTAEMonitor )
+
+UTTAEMonitor::UTTAEMonitor( const std::string& name, ISvcLocator* svcloc )
+    : Consumer{ name,
+                svcloc,
+                { { "InputData", UTDigitLocation::UTDigits },
+                  { "InputErrorData", UTDigitLocation::UTDigits },
+                  { "ODINLocation", LHCb::ODINLocation::Default } } } {}
+
+StatusCode UTTAEMonitor::initialize() {
+  return Consumer::initialize().andThen( [&] {
+    // Set the top directory to UT
+    if ( histoTopDir().empty() ) setHistoTopDir( "UT/" );
+    for ( const auto& DCB : UTMap.getDCBsAside() ) {
+      Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed );
+    }
+    for ( const auto& DCB : UTMap.getDCBsCside() ) {
+      Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed );
+    }
+    for ( const auto& DCBQuadrant : UTMap.getDCBQuadrants() ) {
+      Utility::map_emplace( m_2d_DCBQuadrant_tae_adc, DCBQuadrant, this, DCBQuadrant, AxisTAE, AxisADC_signed );
+    }
+  } );
+}
+
+void UTTAEMonitor::operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont,
+                               LHCb::ODIN const& odin ) const {
+
+  // Loop over UTDigits
+  for ( const auto& d : digitsCont ) { fillHistograms( d, odin ); }
+  // Loop over UTErrDigits
+  for ( const auto& d : errdigitsCont ) { fillHistograms( d, odin ); }
+}
+
+void UTTAEMonitor::fillHistograms( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin ) const {
+
+  // Let's find where we are in UT
+  auto tuple = UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() );
+  std::string                module_name = std::get<4>( tuple );
+  auto                       y_p         = std::get<1>( tuple );
+  std::string                position    = ( y_p > 0 ) ? "T" : "B";
+  std::string                side        = ( aDigit->side() == 0 ) ? "C" : "A";
+  std::array<std::string, 4> layer       = { "UTaX", "UTaU", "UTbV", "UTbX" };
+  std::string                station     = ( aDigit->layer() < 2 ) ? "a" : "b";
+  std::string                dcb_name    = UTMap.getDCB( layer[aDigit->layer()] + "_" + module_name );
+  unsigned int               TAEindex    = odin.timeAlignmentEventIndex();
+
+  // Let's find where we are in the LHCb
+  auto bcid    = odin.bunchId();
+  auto bx      = static_cast<uint8_t>( odin.bunchCrossingType() );
+  auto trgtype = odin.triggerType();
+
+  // simple counters
+  ++m_bxtype[bx];
+  ++m_trgtype[trgtype];
+  ++m_bcid_bxtype[{ bcid, bx }];
+  ++m_bcid_trgtype[{ bcid, trgtype }];
+
+  // summary plots this should be replace by odin.eventType soon as below
+  double weight = 1;
+  int    offset = 0;
+  m_bcid_taesummary[{ bcid, offset + bx }] += weight;
+  offset += 4;
+  m_bcid_taesummary[{ bcid, offset++ }] += weight * odin.isTAE();
+  m_bcid_taesummary[{ bcid, offset++ }] += weight * odin.timeAlignmentEventCentral();
+  m_bcid_taesummary[{ bcid, offset++ }] += weight * odin.timeAlignmentEventFirst();
+
+  // for ( auto const& et : TAEEventTypes ) { m_bcid_taesummary[{bcid, offset++}] += weight * odin.eventTypeBit( et
+  // );}
+
+  ++m_bcid_taeindex[{ bcid, odin.timeAlignmentEventIndex() }];
+
+  if ( TAEindex != 0 ) {
+    int offset = TAEindex - m_HalfWindow - 1; // let's alligne that with superTAE
+    ++m_2d_UT_tae_adc[{ offset, aDigit->depositedCharge() }];
+    ++m_2d_DCB_tae_adc.at( dcb_name )[{ offset, aDigit->depositedCharge() }];
+    ++m_2d_DCBQuadrant_tae_adc.at(
+        UTMap.getDCBQuadrant( layer[aDigit->layer()] + "_" + module_name ) )[{ offset, aDigit->depositedCharge() }];
+  }
+};
diff --git a/UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp b/UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp
new file mode 100644
index 00000000000..eb9a0ad0f69
--- /dev/null
+++ b/UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp
@@ -0,0 +1,134 @@
+/***************************************************************************** \
+* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration      *
+*                                                                             *
+* This software is distributed under the terms of the GNU General Public      *
+* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   *
+*                                                                             *
+* In applying this licence, CERN does not waive the privileges and immunities *
+* granted to it by virtue of its status as an Intergovernmental Organization  *
+* or submit itself to any jurisdiction.                                       *
+\*****************************************************************************/
+
+#include "Event/ODIN.h"
+#include "Event/RawBank.h"
+#include "Event/RawEvent.h"
+#include "Event/UTDigit.h"
+#include "Gaudi/Accumulators/Histogram.h"
+#include "Gaudi/Accumulators/HistogramArray.h"
+#include "GaudiAlg/GaudiHistoAlg.h"
+#include "Kernel/IUTReadoutTool.h"
+#include "Kernel/UTDAQBoard.h"
+#include "Kernel/UTDAQDefinitions.h"
+#include "Kernel/UTDAQID.h"
+#include "LHCbAlgs/Consumer.h"
+#include "LHCbAlgs/Transformer.h"
+#include "UTDAQ/UTCoordinatesMap.h"
+#include "UTDAQ/UTDAQHelper.h"
+#include "UTDAQ/UTInfo.h"
+
+// Needed for VELO
+#include "Event/VPFullCluster.h"
+#include "Kernel/VPConstants.h"
+
+/** @class UTVeloUTCorrelationsMonitor UTVeloUTCorrelationsMonitor.cpp
+ * --------------------------------------------------
+ *  Counters for Monet: correlation monitors
+ *  The algorithm produces correlation plots for hits
+ *  @author Paolo Gandini
+ *  @author Wojciech Krupa
+ *  @date  2024/04/20
+ * --------------------------------------------------
+ **/
+
+using namespace LHCb;
+namespace GA = Gaudi::Accumulators;
+
+// Definition of axis
+const GA::Axis<double> AxisADC        = GA::Axis<double>( 64, -32.5, 31.5, "nADC" );       //-32, 31
+const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 31, -0.5, 31.5, "nADC" );        // 0-31
+const GA::Axis<double> AxisBCID       = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); //
+const GA::Axis<double> AxisDIGIT      = GA::Axis<double>( 1001, -0.5, 10000.5, "# UT" );
+const GA::Axis<double> AxisVeloDIGIT  = GA::Axis<double>( 1001, -0.5, 10000.5, "# Velo" );
+
+// UT layers
+const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" };
+
+enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 };
+template <masks m>
+[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) {
+  constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) );
+  return ( i & static_cast<unsigned int>( m ) ) >> b;
+}
+
+/** ------------------------------------------------------------------------------------------------------------------------ */
+class UTVeloUTCorrelationsMonitor
+    : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, LHCb::ODIN const&,
+                                             std::vector<VPFullCluster> const& ),
+                                       LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg>> {
+public:
+  UTVeloUTCorrelationsMonitor( const std::string& name, ISvcLocator* svcloc )
+      : Consumer{ name,
+                  svcloc,
+                  {
+                      { "InputData", UTDigitLocation::UTDigits },
+                      { "InputErrorData", UTDigitLocation::UTDigits },
+                      { "ODINLocation", LHCb::ODINLocation::Default },
+                      { "VeloClusterLocation", LHCb::VPFullClusterLocation::Default },
+                  } } {}
+  ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" };
+
+  void operator()( const UTDigits&, const UTDigits&, LHCb::ODIN const&,
+                   const std::vector<LHCb::VPFullCluster>& ) const override;
+
+  StatusCode initialize() override {
+    return Consumer::initialize().andThen( [&] {
+      // Set the top directory to UT
+      if ( histoTopDir().empty() ) setHistoTopDir( "" );
+    } );
+  }
+
+private:
+  // General plots
+  mutable GA::Histogram<1> m_nUTDigits{ this, "nUTDigits", "Total UT Digits", AxisDIGIT };
+  mutable GA::Histogram<1> m_nVeloDigits{ this, "nVeloDigits", "Total Velo Digits", AxisVeloDIGIT };
+  mutable GA::Histogram<2> m_nUTnVeloDigits{ this, "nUTnVeloDigits", "nUT vs nVELO", AxisDIGIT, AxisVeloDIGIT };
+  mutable GA::Histogram<2> m_nUTnVeloDigits_bb{ this, "nUTnVeloDigits_bb", "nUT vs nVELO - bb", AxisDIGIT,
+                                                AxisVeloDIGIT };
+  mutable GA::Histogram<2> m_nUTnVeloDigits_be{ this, "nUTnVeloDigits_be", "nUT vs nVELO - be", AxisDIGIT,
+                                                AxisVeloDIGIT };
+  mutable GA::Histogram<2> m_nUTnVeloDigits_eb{ this, "nUTnVeloDigits_eb", "nUT vs nVELO - eb", AxisDIGIT,
+                                                AxisVeloDIGIT };
+  mutable GA::Histogram<2> m_nUTnVeloDigits_ee{ this, "nUTnVeloDigits_ee", "nUT vs nVELO - ee", AxisDIGIT,
+                                                AxisVeloDIGIT };
+
+  mutable GA::HistogramArray<GA::Histogram<2>, 4> m_nUTnVeloDigits_bx{
+      this,
+      []( int i ) { return fmt::format( "nUTnVeloDigits_bx{}", i ); },
+      []( int i ) { return fmt::format( "nUTnVeloDigits BXID{}", i ); },
+      { 201, -0.5, 400.5, "# UT" },
+      { 201, -0.5, 400.5, "# Velo" } };
+
+}; // End of class UTVeloUTCorrelationsMonitor
+DECLARE_COMPONENT( UTVeloUTCorrelationsMonitor )
+
+void UTVeloUTCorrelationsMonitor::operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont,
+                                              LHCb::ODIN const&                       odin,
+                                              const std::vector<LHCb::VPFullCluster>& vpClusters ) const {
+
+  // General counters
+  const unsigned int nBuncId           = odin.bunchId();
+  const int          bunchCrossingType = (int)odin.bunchCrossingType();
+  ++m_nUTDigits[digitsCont.size() + errdigitsCont.size()];
+  ++m_nVeloDigits[vpClusters.size()];
+  ++m_nUTnVeloDigits[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }];
+  ++m_nUTnVeloDigits_bx[bunchCrossingType][{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }];
+
+  if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing )
+    ++m_nUTnVeloDigits_bb[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }];
+  else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 )
+    ++m_nUTnVeloDigits_be[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }];
+  else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 )
+    ++m_nUTnVeloDigits_eb[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }];
+  else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam )
+    ++m_nUTnVeloDigits_ee[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }];
+}
-- 
GitLab