Back to home page

EIC code displayed by LXR

 
 

    


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); // First call to execute() picks up the parent and exits early
0078     auto result = arrow.execute( 0); // Second call to execute() picks up the child, calls Unfold(), and emits the newly parented child
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     // We only use these to obtain preconfigured JEvents
0098     JEventPool parent_pool {jcm, 5, 1, JEventLevel::Timeslice};
0099     JEventPool child_pool {jcm, 5, 1, JEventLevel::PhysicsEvent};
0100 
0101     // We set up our test cases by putting events on these queues
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     // We never insert these hits, and that should be fine 
0209     // because they should never get pushed to the frame
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 * This test case hopefully finds issues that the ePIC timeframe splitter will eventually encounter
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                     // We've reached the last timestep in this timeslice
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                     // Next call to Unfold() starts with the subsequent timestep
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         // No (remaining) hits found in this timeslice for any timestep
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 // JANA2_HAVE_PODIO
0514 
0515 } // namespace unfoldtests
0516 } // namespace jana
0517 
0518 
0519 
0520