Skip to content

Commit 3bb9f02

Browse files
authored
Merge pull request cms-sw#40779 from aloeliger/anomalyDetectionFromExternal
"CICADA"/calorimeter anomaly detection trigger emulator using external repositories
2 parents 8377d6a + 84ea9e1 commit 3bb9f02

File tree

5 files changed

+101
-2
lines changed

5 files changed

+101
-2
lines changed

L1Trigger/L1TCaloLayer1/plugins/BuildFile.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
<use name="DataFormats/L1TCalorimeter"/>
66
<use name="L1Trigger/L1TCalorimeter"/>
77
<use name="L1Trigger/L1TCaloLayer1"/>
8+
<use name="hls"/>
9+
<use name="hls4mlEmulatorExtras"/>
10+
<use name="CICADA"/>
811
<flags EDM_PLUGIN="1"/>

L1Trigger/L1TCaloLayer1/plugins/L1TCaloSummary.cc

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
#include "L1Trigger/L1TCaloLayer1/src/UCTLogging.hh"
5858
#include <bitset>
5959

60+
//Anomaly detection includes
61+
#include "ap_fixed.h"
62+
#include "hls4ml/emulator.h"
63+
6064
using namespace l1tcalo;
6165
using namespace l1extra;
6266
using namespace std;
@@ -102,6 +106,9 @@ class L1TCaloSummary : public edm::stream::EDProducer<> {
102106
edm::EDGetTokenT<L1CaloRegionCollection> regionToken;
103107

104108
UCTLayer1* layer1;
109+
110+
hls4mlEmulator::ModelLoader loader;
111+
std::shared_ptr<hls4mlEmulator::Model> model;
105112
};
106113

107114
//
@@ -127,7 +134,8 @@ L1TCaloSummary::L1TCaloSummary(const edm::ParameterSet& iConfig)
127134
boostedJetPtFactor(iConfig.getParameter<double>("boostedJetPtFactor")),
128135
verbose(iConfig.getParameter<bool>("verbose")),
129136
fwVersion(iConfig.getParameter<int>("firmwareVersion")),
130-
regionToken(consumes<L1CaloRegionCollection>(edm::InputTag("simCaloStage2Layer1Digis"))) {
137+
regionToken(consumes<L1CaloRegionCollection>(edm::InputTag("simCaloStage2Layer1Digis"))),
138+
loader(hls4mlEmulator::ModelLoader(iConfig.getParameter<string>("CICADAModelVersion"))) {
131139
std::vector<double> pumLUTData;
132140
char pumLUTString[10];
133141
for (uint32_t pumBin = 0; pumBin < nPumBins; pumBin++) {
@@ -147,6 +155,10 @@ L1TCaloSummary::L1TCaloSummary(const edm::ParameterSet& iConfig)
147155
}
148156
}
149157
produces<L1JetParticleCollection>("Boosted");
158+
159+
//anomaly trigger loading
160+
model = loader.load_model();
161+
produces<float>("anomalyScore");
150162
}
151163

152164
L1TCaloSummary::~L1TCaloSummary() {}
@@ -161,6 +173,8 @@ void L1TCaloSummary::produce(edm::Event& iEvent, const edm::EventSetup& iSetup)
161173

162174
std::unique_ptr<L1JetParticleCollection> bJetCands(new L1JetParticleCollection);
163175

176+
std::unique_ptr<float> anomalyScore = std::make_unique<float>();
177+
164178
UCTGeometry g;
165179

166180
// Here we read region data from the region collection created by L1TCaloLayer1 instead of
@@ -175,6 +189,11 @@ void L1TCaloSummary::produce(edm::Event& iEvent, const edm::EventSetup& iSetup)
175189
if (!iEvent.getByToken(regionToken, regionCollection))
176190
edm::LogError("L1TCaloSummary") << "UCT: Failed to get regions from region collection!";
177191
iEvent.getByToken(regionToken, regionCollection);
192+
//Model input
193+
//This is done as a flat vector input, but future versions may involve 2D input
194+
//This will have to be handled later
195+
//Would also be good to be able to configure the precision of the ap_fixed type
196+
ap_ufixed<10, 10> modelInput[252];
178197
for (const L1CaloRegion& i : *regionCollection) {
179198
UCTRegionIndex r = g.getUCTRegionIndexFromL1CaloRegion(i.gctEta(), i.gctPhi());
180199
UCTTowerIndex t = g.getUCTTowerIndexFromL1CaloRegion(r, i.raw());
@@ -189,7 +208,22 @@ void L1TCaloSummary::produce(edm::Event& iEvent, const edm::EventSetup& iSetup)
189208
UCTRegion* test = new UCTRegion(crate, card, negativeEta, region, fwVersion);
190209
test->setRegionSummary(i.raw());
191210
inputRegions.push_back(test);
211+
//This *should* fill the tensor in the proper order to be fed to the anomaly model
212+
//We take 4 off of the GCT eta/iEta.
213+
//iEta taken from this ranges from 4-17, (I assume reserving lower and higher for forward regions)
214+
//So our first index, index 0, is technically iEta=4, and so-on.
215+
//CICADA v1 reads this as a flat vector
216+
modelInput[14 * i.gctPhi() + (i.gctEta() - 4)] = i.et();
192217
}
218+
//Extract model output
219+
//Would be good to be able to configure the precision of the result
220+
ap_fixed<11, 5> modelResult[1];
221+
model->prepare_input(modelInput);
222+
model->predict();
223+
model->read_result(modelResult);
224+
225+
*anomalyScore = modelResult[0].to_float();
226+
193227
summaryCard.setRegionData(inputRegions);
194228

195229
if (!summaryCard.process()) {
@@ -253,6 +287,8 @@ void L1TCaloSummary::produce(edm::Event& iEvent, const edm::EventSetup& iSetup)
253287
}
254288

255289
iEvent.put(std::move(bJetCands), "Boosted");
290+
//Write out anomaly score
291+
iEvent.put(std::move(anomalyScore), "anomalyScore");
256292
}
257293

258294
void L1TCaloSummary::print() {}

L1Trigger/L1TCaloLayer1/python/uct2016EmulatorDigis_cfi.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,6 @@
4747
boostedJetPtFactor = cms.double(1.5),
4848
verbose = cms.bool(False),
4949
# See UCTLayer1.hh for firmware version
50-
firmwareVersion = cms.int32(1)
50+
firmwareVersion = cms.int32(1),
51+
CICADAModelVersion = cms.string("CICADAModel_v1")
5152
)

L1Trigger/L1TCaloLayer1/test/BuildFile.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@
77

88
<bin name="testUCTLayer1HF" file="testUCTLayer1HF.cpp">
99
</bin>
10+
11+
<bin name="testCICADAEmulation" file="testCICADAEmulation.cppunit.cc">
12+
<use name="hls"/>
13+
<use name="hls4mlEmulatorExtras"/>
14+
<use name="CICADA"/>
15+
<use name="cppunit"/>
16+
</bin>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//Test of the external CICADA model emulation model loading and model unloading
2+
//Developed by Andrew Loeliger, Princeton University, Feb 23, 2023
3+
4+
//We can't test a load of a bad model here, since that is a segfault, not an exception, which is
5+
//OS level and cppunit cannot test against that in any way that qualifies as a success
6+
7+
//TODO: However, it would be good in the future to assure that loading multiple CICADA models at the
8+
//same time have the correct function symbols assigned to each simultaneously
9+
//i.e. CICADA_v1's predict is not overwritten by CICADA_v2's predict if it is loaded later with a
10+
//CICADA_v1 still around
11+
12+
//TODO: might also be nice to have a test for model integrity? Known test cases producing known outputs?
13+
//This may not be appropriate for unit testing however.
14+
15+
#include "ap_fixed.h"
16+
#include "hls4ml/emulator.h"
17+
18+
#include "cppunit/extensions/HelperMacros.h"
19+
#include <memory>
20+
#include "Utilities/Testing/interface/CppUnit_testdriver.icpp"
21+
22+
class test_CICADA : public CppUnit::TestFixture {
23+
CPPUNIT_TEST_SUITE(test_CICADA);
24+
CPPUNIT_TEST(doModelV1Load);
25+
CPPUNIT_TEST(doModelV2Load);
26+
CPPUNIT_TEST(doMultiModelLoad);
27+
CPPUNIT_TEST_SUITE_END();
28+
29+
public:
30+
void doModelV1Load();
31+
void doModelV2Load();
32+
void doMultiModelLoad();
33+
};
34+
35+
CPPUNIT_TEST_SUITE_REGISTRATION(test_CICADA);
36+
37+
void test_CICADA::doModelV1Load() {
38+
auto loader = hls4mlEmulator::ModelLoader("CICADAModel_v1");
39+
auto model = loader.load_model();
40+
}
41+
42+
void test_CICADA::doModelV2Load() {
43+
auto loader = hls4mlEmulator::ModelLoader("CICADAModel_v2");
44+
auto model = loader.load_model();
45+
}
46+
47+
void test_CICADA::doMultiModelLoad() {
48+
auto loader_v1 = hls4mlEmulator::ModelLoader("CICADAModel_v1");
49+
auto loader_v2 = hls4mlEmulator::ModelLoader("CICADAModel_v2");
50+
auto model_v1 = loader_v1.load_model();
51+
auto model_v2 = loader_v2.load_model();
52+
}

0 commit comments

Comments
 (0)