File indexing completed on 2025-12-19 10:15:53
0001
0002 #include "JANA/JEventUnfolder.h"
0003 #include <JANA/JApplicationFwd.h>
0004 #include <catch.hpp>
0005 #include <JANA/JEventProcessor.h>
0006 #include <JANA/Topology/JUnfoldArrow.h>
0007 #include <JANA/Topology/JFoldArrow.h>
0008 #include <cstdint>
0009
0010 #if JANA2_HAVE_PODIO
0011 #include <PodioDatamodel/ExampleHitCollection.h>
0012 #include <PodioDatamodel/EventInfoCollection.h>
0013 #endif
0014
0015 namespace jana {
0016 namespace unfoldtests {
0017
0018
0019 struct TestUnfolder : public JEventUnfolder {
0020 mutable std::vector<int> preprocessed_event_nrs;
0021 mutable std::vector<JEventLevel> preprocessed_event_levels;
0022 std::vector<int> unfolded_parent_nrs;
0023 std::vector<JEventLevel> unfolded_parent_levels;
0024 std::vector<int> unfolded_child_nrs;
0025 std::vector<JEventLevel> unfolded_child_levels;
0026
0027 TestUnfolder() {
0028 SetParentLevel(JEventLevel::Timeslice);
0029 SetChildLevel(JEventLevel::PhysicsEvent);
0030 }
0031
0032 void Preprocess(const JEvent& parent) const override {
0033 LOG << "Preprocessing " << parent.GetLevel() << " event " << parent.GetEventNumber() << LOG_END;
0034 preprocessed_event_nrs.push_back(parent.GetEventNumber());
0035 preprocessed_event_levels.push_back(parent.GetLevel());
0036 }
0037
0038 Result Unfold(const JEvent& parent, JEvent& child, int iter) override {
0039 auto child_nr = iter + 100 + parent.GetEventNumber();
0040 unfolded_parent_nrs.push_back(parent.GetEventNumber());
0041 unfolded_parent_levels.push_back(parent.GetLevel());
0042 unfolded_child_nrs.push_back(child_nr);
0043 unfolded_child_levels.push_back(child.GetLevel());
0044 child.SetEventNumber(child_nr);
0045 LOG << "Unfolding " << parent.GetLevel() << " event " << parent.GetEventNumber() << " into " << child.GetLevel() << " " << child_nr << "; iter=" << iter << LOG_END;
0046 return (iter == 2 ? Result::NextChildNextParent : Result::NextChildKeepParent);
0047 }
0048 };
0049
0050 TEST_CASE("UnfoldTests_Basic") {
0051
0052 JApplication app;
0053 app.Initialize();
0054 auto jcm = app.GetService<JComponentManager>();
0055
0056 JEventPool parent_pool {jcm, 5, 1, JEventLevel::Timeslice};
0057 JEventPool child_pool {jcm, 5, 1, JEventLevel::PhysicsEvent};
0058 JEventQueue parent_queue {3, 1};
0059 JEventQueue child_queue {3, 1};
0060
0061 auto ts1 = parent_pool.Pop(0);
0062 ts1->SetEventNumber(17);
0063
0064 auto ts2 = parent_pool.Pop(0);
0065 ts2->SetEventNumber(28);
0066
0067 parent_queue.Push(ts1, 0);
0068 parent_queue.Push(ts2, 0);
0069
0070 TestUnfolder unfolder;
0071 JUnfoldArrow arrow("sut", &unfolder);
0072 arrow.attach(&parent_queue, JUnfoldArrow::PARENT_IN);
0073 arrow.attach(&child_pool, JUnfoldArrow::CHILD_IN);
0074 arrow.attach(&child_queue, JUnfoldArrow::CHILD_OUT);
0075
0076 arrow.initialize();
0077 arrow.execute( 0);
0078 auto result = arrow.execute( 0);
0079 REQUIRE(result == JArrow::FireResult::KeepGoing);
0080 REQUIRE(child_queue.GetSize(0) == 1);
0081 REQUIRE(unfolder.preprocessed_event_nrs.size() == 0);
0082 REQUIRE(unfolder.unfolded_parent_nrs.size() == 1);
0083 REQUIRE(unfolder.unfolded_parent_nrs[0] == 17);
0084 REQUIRE(unfolder.unfolded_parent_levels[0] == JEventLevel::Timeslice);
0085 REQUIRE(unfolder.unfolded_child_nrs.size() == 1);
0086 REQUIRE(unfolder.unfolded_child_nrs[0] == 117);
0087 REQUIRE(unfolder.unfolded_child_levels[0] == JEventLevel::PhysicsEvent);
0088
0089 }
0090
0091 TEST_CASE("FoldArrowTests") {
0092
0093 JApplication app;
0094 app.Initialize();
0095 auto jcm = app.GetService<JComponentManager>();
0096
0097
0098 JEventPool parent_pool {jcm, 5, 1, JEventLevel::Timeslice};
0099 JEventPool child_pool {jcm, 5, 1, JEventLevel::PhysicsEvent};
0100
0101
0102 JEventQueue child_in(5, 1);
0103 JEventQueue child_out(5, 1);
0104 JEventQueue parent_out(5, 1);
0105
0106 JFoldArrow arrow("sut", JEventLevel::Timeslice, JEventLevel::PhysicsEvent);
0107 arrow.attach(&child_in, JFoldArrow::CHILD_IN);
0108 arrow.attach(&child_out, JFoldArrow::CHILD_OUT);
0109 arrow.attach(&parent_out, JFoldArrow::PARENT_OUT);
0110 arrow.initialize();
0111
0112 SECTION("One-to-one relationship between timeslices and events") {
0113
0114 auto ts1 = parent_pool.Pop(0);
0115 ts1->SetEventNumber(17);
0116 REQUIRE(ts1->GetLevel() == JEventLevel::Timeslice);
0117
0118 auto ts2 = parent_pool.Pop(0);
0119 ts2->SetEventNumber(28);
0120
0121 auto evt1 = child_pool.Pop(0);
0122 evt1->SetEventNumber(111);
0123
0124 auto evt2 = child_pool.Pop(0);
0125 evt2->SetEventNumber(112);
0126
0127
0128 evt1->SetParent(ts1);
0129 child_in.Push(evt1, 0);
0130
0131 evt2->SetParent(ts2);
0132 child_in.Push(evt2, 0);
0133
0134 arrow.execute(0);
0135
0136 REQUIRE(child_in.GetSize(0) == 1);
0137 REQUIRE(child_out.GetSize(0) == 1);
0138 REQUIRE(parent_out.GetSize(0) == 1);
0139
0140 }
0141
0142
0143 SECTION("One-to-two relationship between timeslices and events") {
0144
0145 auto ts1 = parent_pool.Pop(0);
0146 ts1->SetEventNumber(17);
0147 REQUIRE(ts1->GetLevel() == JEventLevel::Timeslice);
0148
0149 auto ts2 = parent_pool.Pop(0);
0150 ts2->SetEventNumber(28);
0151
0152 auto evt1 = child_pool.Pop(0);
0153 evt1->SetEventNumber(111);
0154
0155 auto evt2 = child_pool.Pop(0);
0156 evt2->SetEventNumber(112);
0157
0158 auto evt3 = child_pool.Pop(0);
0159 evt3->SetEventNumber(113);
0160
0161 auto evt4 = child_pool.Pop(0);
0162 evt4->SetEventNumber(114);
0163
0164
0165 evt1->SetParent(ts1);
0166 evt2->SetParent(ts1);
0167
0168 evt3->SetParent(ts2);
0169 evt4->SetParent(ts2);
0170
0171 child_in.Push(evt1, 0);
0172 child_in.Push(evt2, 0);
0173 child_in.Push(evt3, 0);
0174 child_in.Push(evt4, 0);
0175
0176 arrow.execute(0);
0177
0178 REQUIRE(child_in.GetSize(0) == 3);
0179 REQUIRE(child_out.GetSize(0) == 1);
0180 REQUIRE(parent_out.GetSize(0) == 0);
0181
0182 arrow.execute(0);
0183
0184 REQUIRE(child_in.GetSize(0) == 2);
0185 REQUIRE(child_out.GetSize(0) == 2);
0186 REQUIRE(parent_out.GetSize(0) == 1);
0187
0188 arrow.execute(0);
0189
0190 REQUIRE(child_in.GetSize(0) == 1);
0191 REQUIRE(child_out.GetSize(0) == 3);
0192 REQUIRE(parent_out.GetSize(0) == 1);
0193
0194 arrow.execute(0);
0195
0196 REQUIRE(child_in.GetSize(0) == 0);
0197 REQUIRE(child_out.GetSize(0) == 4);
0198 REQUIRE(parent_out.GetSize(0) == 2);
0199 }
0200
0201
0202 }
0203
0204
0205 class NoOpUnfolder : public JEventUnfolder {
0206 #if JANA2_HAVE_PODIO
0207 PodioOutput<ExampleHit> m_hits_out {this};
0208
0209
0210 #endif
0211
0212 public:
0213 NoOpUnfolder() {
0214 SetParentLevel(JEventLevel::Timeslice);
0215 SetChildLevel(JEventLevel::PhysicsEvent);
0216 }
0217 Result Unfold(const JEvent&, JEvent&, int) {
0218 return Result::KeepChildNextParent;
0219 }
0220 };
0221
0222 class PhysEvtProc : public JEventProcessor {
0223 int m_events_seen = 0;
0224 public:
0225 PhysEvtProc() {
0226 SetCallbackStyle(CallbackStyle::ExpertMode);
0227 }
0228 void ProcessSequential(const JEvent&) {
0229 m_events_seen += 1;
0230 }
0231 void Finish() {
0232 REQUIRE(m_events_seen == 0);
0233 }
0234 };
0235
0236 class TimesliceProc : public JEventProcessor {
0237 int m_events_seen = 0;
0238 public:
0239 TimesliceProc() {
0240 SetLevel(JEventLevel::Timeslice);
0241 SetCallbackStyle(CallbackStyle::ExpertMode);
0242 }
0243 void ProcessSequential(const JEvent&) {
0244 m_events_seen += 1;
0245 }
0246 void Finish() {
0247 REQUIRE(m_events_seen == 10);
0248 }
0249 };
0250
0251 TEST_CASE("NoOpUnfolder_Tests") {
0252 JApplication app;
0253 auto source = new JEventSource();
0254 source->SetLevel(JEventLevel::Timeslice);
0255 app.Add(source);
0256 app.Add(new NoOpUnfolder);
0257 app.Add(new PhysEvtProc);
0258 app.Add(new TimesliceProc);
0259 app.SetParameterValue("jana:nevents", 10);
0260 app.SetParameterValue("jana:loglevel", "debug");
0261 app.Run();
0262 }
0263
0264
0265 #if JANA2_HAVE_PODIO
0266
0267
0268
0269
0270
0271 struct ePICSource : public JEventSource {
0272 std::vector<std::vector<std::tuple<int, int, double>>> datastream;
0273 size_t next_idx = 0;
0274
0275 PodioOutput<EventInfo> info_out {this, "info"};
0276 VariadicPodioOutput<ExampleHit> hits_out {this, {"adet_hits", "bdet_hits"}};
0277
0278 ePICSource() {
0279 SetTypeName("ePICSource");
0280 SetLevel(JEventLevel::Timeslice);
0281 SetCallbackStyle(CallbackStyle::ExpertMode);
0282 }
0283
0284 Result Emit(JEvent&) override {
0285 if (next_idx == datastream.size()) {
0286 return Result::FailureFinished;
0287 }
0288
0289 MutableEventInfo info;
0290 info.TimesliceNumber(next_idx);
0291 info_out()->push_back(info);
0292
0293 const auto& timeslice = datastream.at(next_idx);
0294 for (auto& hit_data: timeslice) {
0295 MutableExampleHit hit;
0296 int det = std::get<0>(hit_data);
0297 int time = std::get<1>(hit_data);
0298 double energy = std::get<2>(hit_data);
0299 hit.time(time);
0300 hit.energy(energy);
0301 hits_out().at(det)->push_back(hit);
0302 }
0303 LOG << "Emitting timeslice " << next_idx << " containing " << timeslice.size() << " hits";
0304 next_idx += 1;
0305
0306 return Result::Success;
0307 }
0308 };
0309
0310 struct TimeAdjustment : public JFactory {
0311 VariadicPodioInput<ExampleHit> hits_in {this, VariadicInputOptions{.names={"adet_hits", "bdet_hits"}}};
0312 VariadicPodioOutput<ExampleHit> hits_out {this, {"adet_hits_adjusted", "bdet_hits_adjusted"}};
0313
0314 TimeAdjustment() {
0315 SetLevel(JEventLevel::Timeslice);
0316 }
0317
0318 void Process(const JEvent&) {
0319 size_t i=0;
0320 for (const auto& hit_coll : hits_in()) {
0321 for (const auto& raw_hit : *hit_coll) {
0322 auto adjusted_hit = raw_hit.clone();
0323 adjusted_hit.time(adjusted_hit.time() + 1);
0324 hits_out().at(i)->push_back(adjusted_hit);
0325 }
0326 i += 1;
0327 }
0328 }
0329 };
0330
0331
0332 struct Splitter : public JEventUnfolder {
0333 PodioInput<EventInfo> info_in {this, {.name="info"}};
0334 PodioOutput<EventInfo> info_out {this, "info"};
0335 VariadicPodioInput<ExampleHit> hits_in {this, VariadicInputOptions{.names={"adet_hits_adjusted", "bdet_hits_adjusted"}}};
0336 VariadicPodioOutput<ExampleHit> hits_out {this, {"adet_hits", "bdet_hits"}};
0337
0338 uint64_t current_t = 0;
0339 uint64_t current_evt_nr = 0;
0340
0341 Splitter() {
0342 info_out.SetSubsetCollection(true);
0343 hits_out.SetSubsetCollection(true);
0344 SetParentLevel(JEventLevel::Timeslice);
0345 SetChildLevel(JEventLevel::PhysicsEvent);
0346 }
0347
0348 JEventUnfolder::Result Unfold(const JEvent&, JEvent& child, int) {
0349
0350 REQUIRE(info_in()->size() == 1);
0351
0352 LOG << "Unfolding timeslice " << info_in()->at(0).TimesliceNumber() << " into physics event " << current_evt_nr;
0353
0354 for (; current_t < 10; current_t += 1) {
0355
0356 bool hits_found = false;
0357 size_t current_coll_idx=0;
0358 for (const auto& hit_coll : hits_in()) {
0359 for (const auto& hit : *hit_coll) {
0360 if (hit.time() == current_t) {
0361 hits_found = true;
0362 hits_out().at(current_coll_idx)->push_back(hit);
0363 }
0364 }
0365 current_coll_idx += 1;
0366 }
0367
0368 if (hits_found) {
0369 if (current_t == 9) {
0370
0371 current_t = 0;
0372 LOG << "Splitter: NextChildNextParent";
0373 info_out()->push_back(info_in()->at(0));
0374 child.SetEventNumber(current_evt_nr++);
0375 return Result::NextChildNextParent;
0376 }
0377 else {
0378
0379 current_t += 1;
0380 LOG << "Splitter: NextChildKeepParent";
0381 info_out()->push_back(info_in()->at(0));
0382 child.SetEventNumber(current_evt_nr++);
0383 return Result::NextChildKeepParent;
0384 }
0385 }
0386 }
0387
0388 current_t = 0;
0389 LOG << "Splitter: KeepChildNextParent";
0390 return Result::KeepChildNextParent;
0391 }
0392 };
0393
0394
0395 struct Checker : public JEventProcessor {
0396 PodioInput<EventInfo> info_in {this, {.name="info"}};
0397 VariadicPodioInput<ExampleHit> hits_in {this, VariadicInputOptions{.names={"adet_hits", "bdet_hits"}}};
0398
0399 std::vector<int> expected_timeslice_nrs;
0400 std::vector<std::vector<std::tuple<int, int, double>>> expected_hits;
0401 size_t current_idx = 0;
0402
0403 Checker() {
0404 SetCallbackStyle(CallbackStyle::ExpertMode);
0405 EnableOrdering();
0406 }
0407 void ProcessSequential(const JEvent& event) override {
0408
0409 int ts_nr = info_in->at(0).TimesliceNumber();
0410 LOG << "Checking event " << event.GetEventNumber() << " from timeslice " << ts_nr;
0411
0412 std::vector<std::tuple<int, int, double>> hits_found;
0413
0414 size_t det_id = 0;
0415 for (const auto& coll : hits_in()) {
0416 for (const auto& hit : *coll) {
0417 hits_found.push_back({ det_id, hit.time(), hit.energy()});
0418 }
0419 det_id += 1;
0420 }
0421
0422 REQUIRE(expected_timeslice_nrs.at(current_idx) == ts_nr);
0423 REQUIRE(expected_hits.at(current_idx).size() == hits_found.size());
0424 for (size_t i=0; i<hits_found.size(); ++i) {
0425 REQUIRE(std::get<0>(expected_hits.at(current_idx).at(i)) == std::get<0>(hits_found.at(i)));
0426 REQUIRE(std::get<1>(expected_hits.at(current_idx).at(i)) == std::get<1>(hits_found.at(i)));
0427 REQUIRE(std::get<2>(expected_hits.at(current_idx).at(i)) == std::get<2>(hits_found.at(i)));
0428 }
0429
0430 current_idx += 1;
0431 }
0432 };
0433
0434
0435 TEST_CASE("ePIC_Timeframe_Splitting") {
0436 JApplication app;
0437 auto src = new ePICSource;
0438 auto checker = new Checker();
0439
0440 app.Add(src);
0441 app.Add(new JFactoryGeneratorT<TimeAdjustment>);
0442 app.Add(new Splitter);
0443 app.Add(checker);
0444
0445 const int DetA = 0;
0446 const int DetB = 1;
0447
0448 SECTION("JustOne") {
0449 src->datastream = {
0450 {{DetA, 0, 22.2}},
0451 {{DetA, 0, 33.3}},
0452 {{DetA, 0, 44.4}}
0453 };
0454 checker->expected_timeslice_nrs = {0, 1, 2};
0455 checker->expected_hits = {
0456 {{DetA, 1, 22.2}},
0457 {{DetA, 1, 33.3}},
0458 {{DetA, 1, 44.4}}
0459 };
0460 app.Run();
0461 }
0462 SECTION("OneOrZero") {
0463 src->datastream = {
0464 {{DetA, 0, 22.2}},
0465 {},
0466 {{DetA, 0, 33.3}},
0467 {{DetA, 0, 44.4}}
0468 };
0469 checker->expected_timeslice_nrs = {0, 2, 3};
0470 checker->expected_hits = {
0471 {{DetA, 1, 22.2}},
0472 {{DetA, 1, 33.3}},
0473 {{DetA, 1, 44.4}}
0474 };
0475 app.Run();
0476 }
0477 SECTION("FlatMap") {
0478 src->datastream = {
0479 {{DetA, 0, 22.2}, {DetA, 1, 33.3}},
0480 {{DetA, 0, 44.4}, {DetA, 1, 55.5}, {DetB, 2, 66.6}},
0481 {{DetA, 0, 77.7}}
0482 };
0483 checker->expected_timeslice_nrs = {0,0,1,1,1,2};
0484 checker->expected_hits = {
0485 {{DetA, 1, 22.2}},
0486 {{DetA, 2, 33.3}},
0487 {{DetA, 1, 44.4}},
0488 {{DetA, 2, 55.5}},
0489 {{DetB, 3, 66.6}},
0490 {{DetA, 1, 77.7}}
0491 };
0492 app.Run();
0493 }
0494 SECTION("UnFlatMap") {
0495 src->datastream = {
0496 {{DetA, 0, 22.2}, {DetA, 1, 33.3}, {DetA, 1, 19}, {DetB, 1, 22}},
0497 {{DetA, 0, 44.4}, {DetA, 1, 55.5}, {DetB, 2, 66.6}},
0498 {{DetA, 0, 77.7}, {DetA, 0, 88.8}, {DetA, 0, 99.9}}
0499 };
0500 checker->expected_timeslice_nrs = {0,0,1,1,1,2};
0501 checker->expected_hits = {
0502 {{DetA, 1, 22.2}},
0503 {{DetA, 2, 33.3}, {DetA, 2, 19}, {DetB, 2, 22}},
0504 {{DetA, 1, 44.4}},
0505 {{DetA, 2, 55.5}},
0506 {{DetB, 3, 66.6}},
0507 {{DetA, 1, 77.7}, {DetA, 1, 88.8}, {DetA, 1, 99.9}}
0508 };
0509 app.Run();
0510 }
0511 }
0512
0513 #endif
0514
0515 }
0516 }
0517
0518
0519
0520