diff --git a/.github/halld_recon_build.sh b/.github/halld_recon_build.sh index e2c405132..0d4d59b7d 100644 --- a/.github/halld_recon_build.sh +++ b/.github/halld_recon_build.sh @@ -7,15 +7,9 @@ mkdir -p /cvmfs/oasis.opensciencegrid.org mount -t cvmfs oasis.opensciencegrid.org /cvmfs/oasis.opensciencegrid.org ln -s /cvmfs/oasis.opensciencegrid.org/gluex/group /group -export BMS_OSNAME_OVERRIDE="Linux_Alma9-x86_64-gcc11.4.1-cntr" -# The BMS_OSNAME override is needed because Alma9 retroactively switched its system gcc -# from 11.4.1 to 11.5. We cannot create a new Alma9 container that uses gcc11.4.1, but -# the CVMFS artifact repository hasn't been updated to reflect that. - source /group/halld/Software/build_scripts/gluex_env_boot_jlab.sh -gxenv /workspace/JANA2/.github/halld_recon_build_prereqs_version.xml +gxenv /workspace/JANA2/.github/version.xml echo "ROOTSYS=$ROOTSYS" cd /workspace/halld_recon/src scons install -j12 DEBUG=1 OPTIMIZATION=0 SHOWBUILD=1 - diff --git a/.github/halld_recon_build_prereqs_version.xml b/.github/halld_recon_build_prereqs_version.xml deleted file mode 100644 index 9a43c1c81..000000000 --- a/.github/halld_recon_build_prereqs_version.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - -Update to amptools, gluex_root_analysis, halld_recont, halld_sim, hdgeant4, hd_utilities - - - - - - - - - - - - \ No newline at end of file diff --git a/.github/jana_build.sh b/.github/jana_build.sh index 5672f6d1b..5647fcc9c 100644 --- a/.github/jana_build.sh +++ b/.github/jana_build.sh @@ -13,7 +13,7 @@ export JANA_HOME=$PROJECT_ROOT/JANA2 export JANA_PLUGIN_PATH=$PROJECT_ROOT/JANA2/plugins source $BUILD_SCRIPTS/gluex_env_boot_jlab.sh --bs $BUILD_SCRIPTS -gxenv $PROJECT_ROOT/JANA2/.github/jana_prereqs_version.xml +gxenv $PROJECT_ROOT/JANA2/.github/version.xml echo "jana_home value: $JANA_HOME" diff --git a/.github/jana_prereqs_version.xml b/.github/jana_prereqs_version.xml deleted file mode 100644 index f40682d12..000000000 --- a/.github/jana_prereqs_version.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - -Update to amptools, gluex_root_analysis, halld_recont, halld_sim, hdgeant4, hd_utilities - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/version.xml b/.github/version.xml new file mode 100644 index 000000000..922c2eeca --- /dev/null +++ b/.github/version.xml @@ -0,0 +1,28 @@ + + + +Bugfix in halld_recon, based on root6.32.08 and compiled with c++20. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/workflows/halld_recon.yml b/.github/workflows/halld_recon.yml index 0fccf8988..19b3aaffd 100644 --- a/.github/workflows/halld_recon.yml +++ b/.github/workflows/halld_recon.yml @@ -28,7 +28,7 @@ jobs: --platform linux/amd64 \ --privileged \ --mount type=bind,source=${{ github.workspace }},target=/workspace \ - raiqarasool/gluex_build:cvmfs /bin/bash -c "source /workspace/JANA2/.github/jana_build.sh" + codecr.jlab.org/nbrei/jana2-ci/jana2_devenv_alma9:latest /bin/bash -c "source /workspace/JANA2/.github/jana_build.sh" - name: Git Clone Halld_recon run: | @@ -42,7 +42,7 @@ jobs: --platform linux/amd64 \ --privileged \ --mount type=bind,source=${{ github.workspace }},target=/workspace \ - raiqarasool/gluex_build:cvmfs /bin/bash -c "source /workspace/JANA2/.github/halld_recon_build.sh" + codecr.jlab.org/nbrei/jana2-ci/jana2_devenv_alma9:latest /bin/bash -c "source /workspace/JANA2/.github/halld_recon_build.sh" - name: Cleaning up created folders if: always() diff --git a/src/libraries/JANA/CMakeLists.txt b/src/libraries/JANA/CMakeLists.txt index e423cda2e..14457ea11 100644 --- a/src/libraries/JANA/CMakeLists.txt +++ b/src/libraries/JANA/CMakeLists.txt @@ -32,6 +32,7 @@ set(JANA2_SOURCES Components/JComponentSummary.cc Components/JDatabundle.cc Components/JHasInputs.cc + Components/JHasOutputs.cc Utils/JCpuInfo.cc Utils/JProcessorMapping.cc diff --git a/src/libraries/JANA/Components/JHasInputs.cc b/src/libraries/JANA/Components/JHasInputs.cc index ef65ba015..92bd8d60c 100644 --- a/src/libraries/JANA/Components/JHasInputs.cc +++ b/src/libraries/JANA/Components/JHasInputs.cc @@ -57,7 +57,7 @@ void JHasInputs::VariadicInputBase::TriggerFactoryCreate(const JEvent& event) { } throw JException("Could not find parent at level=" + toString(m_level)); } - if (!m_realized_databundle_names.empty()) { + if (!m_requested_databundle_names.empty()) { for (auto& tag : m_requested_databundle_names) { auto coll = facset->GetDatabundle(m_type_index, tag); if (coll == nullptr && !m_is_optional) { diff --git a/src/libraries/JANA/Components/JHasInputs.h b/src/libraries/JANA/Components/JHasInputs.h index 0bce3c4bf..64918fd2b 100644 --- a/src/libraries/JANA/Components/JHasInputs.h +++ b/src/libraries/JANA/Components/JHasInputs.h @@ -139,6 +139,9 @@ struct JHasInputs { void SetRequestedDatabundleNames(std::vector names) { m_requested_databundle_names = names; m_realized_databundle_names = names; + // If options.names are empty, m_realized_databundle_names will be filled later + // Otherwise, m_realized_databundle_names always matches m_requested_databundle_names + // This weirdness is an optimization to avoid having to repopulate m_realized_databundle_names for every event } void SetEmptyInputPolicy(EmptyInputPolicy policy) { @@ -163,6 +166,10 @@ struct JHasInputs { void Configure(const VariadicInputOptions& options) { m_requested_databundle_names = options.names; + m_realized_databundle_names = options.names; + // If options.names are empty, m_realized_databundle_names will be filled later + // Otherwise, m_realized_databundle_names always matches m_requested_databundle_names + // This weirdness is an optimization to avoid having to repopulate m_realized_databundle_names for every event m_level = options.level; m_is_optional = options.is_optional; } @@ -339,6 +346,7 @@ struct JHasInputs { void SetTags(std::vector tags) { m_requested_databundle_names = tags; + m_realized_databundle_names = tags; } const std::vector>& operator()() { return m_datas; } @@ -526,37 +534,44 @@ struct JHasInputs { return; } - // Validate that we have the correct number of input databundle names - if (single_input_databundle_names.size() != m_inputs.size()) { - throw JException("Wrong number of (nonvariadic) input databundle names! Expected %d, found %d", m_inputs.size(), single_input_databundle_names.size()); - } - - if (variadic_input_databundle_names.size() != m_variadic_inputs.size()) { - throw JException("Wrong number of variadic input databundle names! Expected %d, found %d", m_variadic_inputs.size(), variadic_input_databundle_names.size()); - } + if (!single_input_databundle_names.empty()) { - size_t i = 0; - for (auto* input : m_inputs) { - input->SetDatabundleName(single_input_databundle_names.at(i)); - if (single_input_levels.empty()) { - input->SetLevel(component_level); + // Validate that we have the correct number of input databundle names + if (single_input_databundle_names.size() != m_inputs.size()) { + throw JException("Wrong number of (nonvariadic) input databundle names! Expected %d, found %d", m_inputs.size(), single_input_databundle_names.size()); } - else { - input->SetLevel(single_input_levels.at(i)); + + size_t i = 0; + for (auto* input : m_inputs) { + input->SetDatabundleName(single_input_databundle_names.at(i)); + if (single_input_levels.empty()) { + input->SetLevel(component_level); + } + else { + input->SetLevel(single_input_levels.at(i)); + } + i += 1; } - i += 1; } - i = 0; - for (auto* variadic_input : m_variadic_inputs) { - variadic_input->SetRequestedDatabundleNames(variadic_input_databundle_names.at(i)); - if (variadic_input_levels.empty()) { - variadic_input->SetLevel(component_level); + if (!variadic_input_databundle_names.empty()) { + + // Validate that we have the correct number of variadic input databundle names + if (variadic_input_databundle_names.size() != m_variadic_inputs.size()) { + throw JException("Wrong number of lists of variadic input databundle names! Expected %d, found %d", m_variadic_inputs.size(), variadic_input_databundle_names.size()); } - else { - variadic_input->SetLevel(variadic_input_levels.at(i)); + + size_t i = 0; + for (auto* variadic_input : m_variadic_inputs) { + variadic_input->SetRequestedDatabundleNames(variadic_input_databundle_names.at(i)); + if (variadic_input_levels.empty()) { + variadic_input->SetLevel(component_level); + } + else { + variadic_input->SetLevel(variadic_input_levels.at(i)); + } + i += 1; } - i += 1; } } diff --git a/src/libraries/JANA/Components/JHasOutputs.cc b/src/libraries/JANA/Components/JHasOutputs.cc new file mode 100644 index 000000000..e430f0c45 --- /dev/null +++ b/src/libraries/JANA/Components/JHasOutputs.cc @@ -0,0 +1,15 @@ +#include "JHasOutputs.h" +#include + +void jana::components::UpdateFactoryStatusOnEulerianStore(JFactory* fac) { + // We need to set the factory status separately from the databundle status so + // that the factory doesn't accidentally get re-run. + // We do this inside a weird little free function because we need to avoid creating + // a circular definition of JFactory in our templates. + // Eventually we will need to refactor JFactory::Status and CreationStatus. + + fac->SetStatus(JFactory::Status::Inserted); + fac->SetCreationStatus(JFactory::CreationStatus::Inserted); +} + + diff --git a/src/libraries/JANA/Components/JHasOutputs.h b/src/libraries/JANA/Components/JHasOutputs.h index 724bfbdcd..c9d88e611 100644 --- a/src/libraries/JANA/Components/JHasOutputs.h +++ b/src/libraries/JANA/Components/JHasOutputs.h @@ -8,6 +8,7 @@ class JFactorySet; namespace jana::components { +void UpdateFactoryStatusOnEulerianStore(JFactory* fac); class JHasOutputs { public: @@ -39,7 +40,7 @@ class JHasOutputs { class VariadicOutputBase { private: std::vector m_databundles; - JEventLevel m_level = JEventLevel::PhysicsEvent; + JEventLevel m_level = JEventLevel::None; public: virtual ~VariadicOutputBase() { @@ -167,27 +168,39 @@ class JHasOutputs { } else { // Do the obvious, sensible thing instead - size_t i = 0; - for (auto* output : m_outputs) { - output->SetLevel(component_level); - if (use_short_names) { - output->SetShortName(single_output_databundle_names.at(i)); + if (!single_output_databundle_names.empty()) { + if (single_output_databundle_names.size() != m_outputs.size()) { + throw JException("Wrong number of (nonvariadic) output databundle names! Expected %d, found %d", m_outputs.size(), single_output_databundle_names.size()); } - else { - output->SetUniqueName(single_output_databundle_names.at(i)); + + size_t i = 0; + for (auto* output : m_outputs) { + output->SetLevel(component_level); + if (use_short_names) { + output->SetShortName(single_output_databundle_names.at(i)); + } + else { + output->SetUniqueName(single_output_databundle_names.at(i)); + } + i += 1; } - i += 1; } - i = 0; - for (auto* variadic_output : m_variadic_outputs) { - variadic_output->SetLevel(component_level); - if (use_short_names) { - variadic_output->SetShortNames(variadic_output_databundle_names.at(i)); + + if (!variadic_output_databundle_names.empty()) { + if (variadic_output_databundle_names.size() != m_variadic_outputs.size()) { + throw JException("Wrong number of lists of variadic output databundle names! Expected %d, found %d", m_variadic_outputs.size(), variadic_output_databundle_names.size()); } - else { - variadic_output->SetUniqueNames(variadic_output_databundle_names.at(i)); + size_t i = 0; + for (auto* variadic_output : m_variadic_outputs) { + variadic_output->SetLevel(component_level); + if (use_short_names) { + variadic_output->SetShortNames(variadic_output_databundle_names.at(i)); + } + else { + variadic_output->SetUniqueNames(variadic_output_databundle_names.at(i)); + } + i += 1; } - i += 1; } } } diff --git a/src/libraries/JANA/Components/JLightweightOutput.h b/src/libraries/JANA/Components/JLightweightOutput.h index 733b9d07d..78dbe6393 100644 --- a/src/libraries/JANA/Components/JLightweightOutput.h +++ b/src/libraries/JANA/Components/JLightweightOutput.h @@ -74,6 +74,10 @@ class Output : public JHasOutputs::OutputBase { typed_bundle->GetData() = std::move(*m_external_data); } typed_bundle->SetStatus(JDatabundle::Status::Inserted); + auto fac = typed_bundle->GetFactory(); + if (fac != nullptr) { + UpdateFactoryStatusOnEulerianStore(fac); + } } }; @@ -179,6 +183,11 @@ class VariadicOutput : public JHasOutputs::VariadicOutputBase { } typed_databundle->GetData() = std::move(m_transient_datas.at(i)); typed_databundle->SetStatus(JDatabundle::Status::Inserted); + + auto fac = typed_databundle->GetFactory(); + if (fac != nullptr) { + UpdateFactoryStatusOnEulerianStore(fac); + } i += 1; } } diff --git a/src/libraries/JANA/Components/JPodioOutput.h b/src/libraries/JANA/Components/JPodioOutput.h index 7e4717408..b9a7ef3b4 100644 --- a/src/libraries/JANA/Components/JPodioOutput.h +++ b/src/libraries/JANA/Components/JPodioOutput.h @@ -25,7 +25,7 @@ class PodioOutput : public JHasOutputs::OutputBase { this->m_podio_databundle = new JPodioDatabundle; SetDatabundle(m_podio_databundle); - m_podio_databundle->SetShortName(collection_name); + m_podio_databundle->SetUniqueName(collection_name); m_podio_databundle->SetTypeName(JTypeInfo::demangle()); m_podio_databundle->SetTypeIndex(std::type_index(typeid(PodioT))); @@ -130,6 +130,12 @@ class PodioOutput : public JHasOutputs::OutputBase { } typed_collection_bundle->SetCollection(published); typed_collection_bundle->SetStatus(JDatabundle::Status::Inserted); + + auto fac = typed_collection_bundle->GetFactory(); + if (fac != nullptr) { + UpdateFactoryStatusOnEulerianStore(fac); + } + m_transient_collection = std::make_unique(); m_transient_collection->setSubsetCollection(m_is_subset); } @@ -140,7 +146,7 @@ template class VariadicPodioOutput : public JHasOutputs::VariadicOutputBase { private: std::vector> m_transient_collections; - std::vector m_databundles; + bool m_is_subset = false; public: VariadicPodioOutput(JHasOutputs* owner, std::vector default_collection_names={}) { @@ -151,13 +157,15 @@ class VariadicPodioOutput : public JHasOutputs::VariadicOutputBase { databundle->SetTypeName(JTypeInfo::demangle()); databundle->SetTypeIndex(std::type_index(typeid(PodioT))); GetDatabundles().push_back(databundle); - m_databundles.push_back(databundle); m_transient_collections.push_back(std::make_unique()); } } void SetShortNames(std::vector short_names) override { - m_databundles.clear(); + for (auto* db : GetDatabundles()) { + delete db; + } + GetDatabundles().clear(); m_transient_collections.clear(); for (const std::string& name : short_names) { auto databundle = new JPodioDatabundle; @@ -165,27 +173,41 @@ class VariadicPodioOutput : public JHasOutputs::VariadicOutputBase { databundle->SetTypeName(JTypeInfo::demangle()); databundle->SetTypeIndex(std::type_index(typeid(PodioT))); GetDatabundles().push_back(databundle); - m_databundles.push_back(databundle); m_transient_collections.push_back(std::make_unique()); + m_transient_collections.back()->setSubsetCollection(m_is_subset); } } void SetUniqueNames(std::vector unique_names) override { - m_databundles.clear(); + for (auto* db : GetDatabundles()) { + delete db; + } + GetDatabundles().clear(); m_transient_collections.clear(); for (const std::string& name : unique_names) { auto databundle = new JPodioDatabundle; + LOG << "SetUniqueNames: Adding databundle with unique_name " << name; databundle->SetUniqueName(name); databundle->SetTypeName(JTypeInfo::demangle()); databundle->SetTypeIndex(std::type_index(typeid(PodioT))); GetDatabundles().push_back(databundle); - m_databundles.push_back(databundle); m_transient_collections.push_back(std::make_unique()); + m_transient_collections.back()->setSubsetCollection(m_is_subset); } } std::vector>& operator()() { return m_transient_collections; } + void SetSubsetCollection(bool is_subset) { + m_is_subset = is_subset; + for (auto& coll : m_transient_collections) { + coll->setSubsetCollection(is_subset); + } + } + + bool IsSubsetCollection() const { return m_is_subset; } + + void LagrangianStore(JFactorySet& facset, JDatabundle::Status status) override { if (m_transient_collections.size() != GetDatabundles().size()) { throw JException("VariadicPodioOutput::LagrangianStore() failed: Declared %d collections, but provided %d.", GetDatabundles().size(), m_transient_collections.size()); @@ -208,13 +230,14 @@ class VariadicPodioOutput : public JHasOutputs::VariadicOutputBase { size_t i = 0; for (auto& collection : m_transient_collections) { - frame->put(std::move(collection), m_databundles[i]->GetUniqueName()); - const auto* moved = &frame->template get(m_databundles[i]->GetUniqueName()); - const auto &databundle = dynamic_cast(m_databundles[i]); + frame->put(std::move(collection), GetDatabundles()[i]->GetUniqueName()); + const auto* moved = &frame->template get(GetDatabundles()[i]->GetUniqueName()); + const auto &databundle = dynamic_cast(GetDatabundles()[i]); databundle->SetCollection(moved); databundle->SetStatus(status); i += 1; collection = std::make_unique(); + collection->setSubsetCollection(m_is_subset); } } @@ -246,31 +269,40 @@ class VariadicPodioOutput : public JHasOutputs::VariadicOutputBase { int i=0; for (auto& collection : m_transient_collections) { - frame->put(std::move(collection), m_databundles[i]->GetUniqueName()); - const auto* moved = &frame->template get(m_databundles[i]->GetUniqueName()); + frame->put(std::move(collection), GetDatabundles()[i]->GetUniqueName()); + const auto* moved = &frame->template get(GetDatabundles()[i]->GetUniqueName()); JPodioDatabundle* typed_collection_bundle = nullptr; - auto collection_bundle = facset.GetDatabundle(m_databundles[i]->GetTypeIndex(), m_databundles[i]->GetUniqueName()); + auto collection_bundle = facset.GetDatabundle(GetDatabundles()[i]->GetTypeIndex(), GetDatabundles()[i]->GetUniqueName()); if (collection_bundle == nullptr) { // No databundle present. In this case we create it, using m_databundles[i] as a template - typed_collection_bundle = new JPodioDatabundle(*m_databundles[i]); + auto typed_prototype = static_cast(GetDatabundles()[i]); + typed_collection_bundle = new JPodioDatabundle(*typed_prototype); facset.Add(typed_collection_bundle); } else { typed_collection_bundle = dynamic_cast(collection_bundle); if (typed_collection_bundle == nullptr) { // Wrong databundle present - throw JException("Databundle with unique_name '%s' is not a JPodioDatabundle", m_databundles[i]->GetUniqueName().c_str()); + throw JException("Databundle with unique_name '%s' is not a JPodioDatabundle", GetDatabundles()[i]->GetUniqueName().c_str()); } } // Then we store the collection itself typed_collection_bundle->SetCollection(moved); typed_collection_bundle->SetStatus(JDatabundle::Status::Inserted); + + auto fac = typed_collection_bundle->GetFactory(); + if (fac != nullptr) { + UpdateFactoryStatusOnEulerianStore(fac); + } + + // Replace the transient collection + collection = std::make_unique(); + collection->setSubsetCollection(m_is_subset); i += 1; } - m_transient_collections.clear(); } }; diff --git a/src/libraries/JANA/Services/JWiringService.h b/src/libraries/JANA/Services/JWiringService.h index 47a0ad691..64b456ece 100644 --- a/src/libraries/JANA/Services/JWiringService.h +++ b/src/libraries/JANA/Services/JWiringService.h @@ -43,7 +43,7 @@ class JWiringService : public JService { }; private: - Parameter m_wirings_input_file {this, "jana:wiring_file", "", + Parameter m_wirings_input_file {this, "wiring_file", "", "Path to TOML file containing wiring definitions"}; WiringSet m_wiring_set; diff --git a/src/libraries/JANA/Topology/JEventMapArrow.cc b/src/libraries/JANA/Topology/JEventMapArrow.cc index ac54ea115..72d43317d 100644 --- a/src/libraries/JANA/Topology/JEventMapArrow.cc +++ b/src/libraries/JANA/Topology/JEventMapArrow.cc @@ -38,7 +38,7 @@ void JEventMapArrow::fire(JEvent* event, OutputData& outputs, size_t& output_cou } for (JEventUnfolder* unfolder : m_unfolders) { JCallGraphEntryMaker cg_entry(*event->GetJCallGraphRecorder(), unfolder->GetTypeName()); // times execution until this goes out of scope - unfolder->Preprocess(*event); + unfolder->DoPreprocess(*event); } for (JEventProcessor* processor : m_procs) { JCallGraphEntryMaker cg_entry(*event->GetJCallGraphRecorder(), processor->GetTypeName()); // times execution until this goes out of scope diff --git a/src/programs/unit_tests/Components/UnfoldTests.cc b/src/programs/unit_tests/Components/UnfoldTests.cc index 38038ae15..ef7736ac2 100644 --- a/src/programs/unit_tests/Components/UnfoldTests.cc +++ b/src/programs/unit_tests/Components/UnfoldTests.cc @@ -1,11 +1,15 @@ +#include "JANA/JEventUnfolder.h" +#include #include #include #include #include +#include #if JANA2_HAVE_PODIO #include +#include #endif namespace jana { @@ -258,7 +262,257 @@ TEST_CASE("NoOpUnfolder_Tests") { } -} // namespace arrowtests +#if JANA2_HAVE_PODIO + +/* +* This test case hopefully finds issues that the ePIC timeframe splitter will eventually encounter +*/ + +struct ePICSource : public JEventSource { + std::vector>> datastream; + size_t next_idx = 0; + + PodioOutput info_out {this, "info"}; + VariadicPodioOutput hits_out {this, {"adet_hits", "bdet_hits"}}; + + ePICSource() { + SetTypeName("ePICSource"); + SetLevel(JEventLevel::Timeslice); + SetCallbackStyle(CallbackStyle::ExpertMode); + } + + Result Emit(JEvent&) override { + if (next_idx == datastream.size()) { + return Result::FailureFinished; + } + + MutableEventInfo info; + info.TimesliceNumber(next_idx); + info_out()->push_back(info); + + const auto& timeslice = datastream.at(next_idx); + for (auto& hit_data: timeslice) { + MutableExampleHit hit; + int det = std::get<0>(hit_data); + int time = std::get<1>(hit_data); + double energy = std::get<2>(hit_data); + hit.time(time); + hit.energy(energy); + hits_out().at(det)->push_back(hit); + } + LOG << "Emitting timeslice " << next_idx << " containing " << timeslice.size() << " hits"; + next_idx += 1; + + return Result::Success; + } +}; + +struct TimeAdjustment : public JFactory { + VariadicPodioInput hits_in {this, VariadicInputOptions{.names={"adet_hits", "bdet_hits"}}}; + VariadicPodioOutput hits_out {this, {"adet_hits_adjusted", "bdet_hits_adjusted"}}; + + TimeAdjustment() { + SetLevel(JEventLevel::Timeslice); + } + + void Process(const JEvent&) { + size_t i=0; + for (const auto& hit_coll : hits_in()) { + for (const auto& raw_hit : *hit_coll) { + auto adjusted_hit = raw_hit.clone(); + adjusted_hit.time(adjusted_hit.time() + 1); + hits_out().at(i)->push_back(adjusted_hit); + } + i += 1; + } + } +}; + + +struct Splitter : public JEventUnfolder { + PodioInput info_in {this, {.name="info"}}; + PodioOutput info_out {this, "info"}; + VariadicPodioInput hits_in {this, VariadicInputOptions{.names={"adet_hits_adjusted", "bdet_hits_adjusted"}}}; + VariadicPodioOutput hits_out {this, {"adet_hits", "bdet_hits"}}; + + uint64_t current_t = 0; + uint64_t current_evt_nr = 0; + + Splitter() { + info_out.SetSubsetCollection(true); + hits_out.SetSubsetCollection(true); + SetParentLevel(JEventLevel::Timeslice); + SetChildLevel(JEventLevel::PhysicsEvent); + } + + JEventUnfolder::Result Unfold(const JEvent&, JEvent& child, int) { + + REQUIRE(info_in()->size() == 1); + + LOG << "Unfolding timeslice " << info_in()->at(0).TimesliceNumber() << " into physics event " << current_evt_nr; + + for (; current_t < 10; current_t += 1) { + + bool hits_found = false; + size_t current_coll_idx=0; + for (const auto& hit_coll : hits_in()) { + for (const auto& hit : *hit_coll) { + if (hit.time() == current_t) { + hits_found = true; + hits_out().at(current_coll_idx)->push_back(hit); + } + } + current_coll_idx += 1; + } + + if (hits_found) { + if (current_t == 9) { + // We've reached the last timestep in this timeslice + current_t = 0; + LOG << "Splitter: NextChildNextParent"; + info_out()->push_back(info_in()->at(0)); + child.SetEventNumber(current_evt_nr++); + return Result::NextChildNextParent; + } + else { + // Next call to Unfold() starts with the subsequent timestep + current_t += 1; + LOG << "Splitter: NextChildKeepParent"; + info_out()->push_back(info_in()->at(0)); + child.SetEventNumber(current_evt_nr++); + return Result::NextChildKeepParent; + } + } + } + // No (remaining) hits found in this timeslice for any timestep + current_t = 0; + LOG << "Splitter: KeepChildNextParent"; + return Result::KeepChildNextParent; + } +}; + + +struct Checker : public JEventProcessor { + PodioInput info_in {this, {.name="info"}}; + VariadicPodioInput hits_in {this, VariadicInputOptions{.names={"adet_hits", "bdet_hits"}}}; + + std::vector expected_timeslice_nrs; + std::vector>> expected_hits; + size_t current_idx = 0; + + Checker() { + SetCallbackStyle(CallbackStyle::ExpertMode); + EnableOrdering(); + } + void ProcessSequential(const JEvent& event) override { + + int ts_nr = info_in->at(0).TimesliceNumber(); + LOG << "Checking event " << event.GetEventNumber() << " from timeslice " << ts_nr; + + std::vector> hits_found; + + size_t det_id = 0; + for (const auto& coll : hits_in()) { + for (const auto& hit : *coll) { + hits_found.push_back({ det_id, hit.time(), hit.energy()}); + } + det_id += 1; + } + + REQUIRE(expected_timeslice_nrs.at(current_idx) == ts_nr); + REQUIRE(expected_hits.at(current_idx).size() == hits_found.size()); + for (size_t i=0; i(expected_hits.at(current_idx).at(i)) == std::get<0>(hits_found.at(i))); + REQUIRE(std::get<1>(expected_hits.at(current_idx).at(i)) == std::get<1>(hits_found.at(i))); + REQUIRE(std::get<2>(expected_hits.at(current_idx).at(i)) == std::get<2>(hits_found.at(i))); + } + + current_idx += 1; + } +}; + + +TEST_CASE("ePIC_Timeframe_Splitting") { + JApplication app; + auto src = new ePICSource; + auto checker = new Checker(); + + app.Add(src); + app.Add(new JFactoryGeneratorT); + app.Add(new Splitter); + app.Add(checker); + + const int DetA = 0; + const int DetB = 1; + + SECTION("JustOne") { + src->datastream = { + {{DetA, 0, 22.2}}, + {{DetA, 0, 33.3}}, + {{DetA, 0, 44.4}} + }; + checker->expected_timeslice_nrs = {0, 1, 2}; + checker->expected_hits = { + {{DetA, 1, 22.2}}, + {{DetA, 1, 33.3}}, + {{DetA, 1, 44.4}} + }; + app.Run(); + } + SECTION("OneOrZero") { + src->datastream = { + {{DetA, 0, 22.2}}, + {}, + {{DetA, 0, 33.3}}, + {{DetA, 0, 44.4}} + }; + checker->expected_timeslice_nrs = {0, 2, 3}; + checker->expected_hits = { + {{DetA, 1, 22.2}}, + {{DetA, 1, 33.3}}, + {{DetA, 1, 44.4}} + }; + app.Run(); + } + SECTION("FlatMap") { + src->datastream = { + {{DetA, 0, 22.2}, {DetA, 1, 33.3}}, + {{DetA, 0, 44.4}, {DetA, 1, 55.5}, {DetB, 2, 66.6}}, + {{DetA, 0, 77.7}} + }; + checker->expected_timeslice_nrs = {0,0,1,1,1,2}; + checker->expected_hits = { + {{DetA, 1, 22.2}}, + {{DetA, 2, 33.3}}, + {{DetA, 1, 44.4}}, + {{DetA, 2, 55.5}}, + {{DetB, 3, 66.6}}, + {{DetA, 1, 77.7}} + }; + app.Run(); + } + SECTION("UnFlatMap") { + src->datastream = { + {{DetA, 0, 22.2}, {DetA, 1, 33.3}, {DetA, 1, 19}, {DetB, 1, 22}}, + {{DetA, 0, 44.4}, {DetA, 1, 55.5}, {DetB, 2, 66.6}}, + {{DetA, 0, 77.7}, {DetA, 0, 88.8}, {DetA, 0, 99.9}} + }; + checker->expected_timeslice_nrs = {0,0,1,1,1,2}; + checker->expected_hits = { + {{DetA, 1, 22.2}}, + {{DetA, 2, 33.3}, {DetA, 2, 19}, {DetB, 2, 22}}, + {{DetA, 1, 44.4}}, + {{DetA, 2, 55.5}}, + {{DetB, 3, 66.6}}, + {{DetA, 1, 77.7}, {DetA, 1, 88.8}, {DetA, 1, 99.9}} + }; + app.Run(); + } +} + +#endif // JANA2_HAVE_PODIO + +} // namespace unfoldtests } // namespace jana diff --git a/src/programs/unit_tests/Services/JWiringServiceTests.cc b/src/programs/unit_tests/Services/JWiringServiceTests.cc index b07b3bc92..d700470cb 100644 --- a/src/programs/unit_tests/Services/JWiringServiceTests.cc +++ b/src/programs/unit_tests/Services/JWiringServiceTests.cc @@ -433,6 +433,8 @@ struct WiredFac : public JFactory { WiredFac() { SetPrefix("wiredfac"); + m_protoclusters_in.SetDatabundleName("default_protos"); + m_clusters_out.SetUniqueName("default_clusters"); } void Process(const JEvent&) { for (auto protocluster : *m_protoclusters_in) { @@ -525,4 +527,64 @@ TEST_CASE("WiringTests_FacGenShortnames") { } +static constexpr std::string_view facgen_partial_input_names_only = R"( + [[wiring]] + action = "update" + type_name = "WiredFac" + prefix = "wiredfac" + input_names = ["overridden_input"] +)"; + +TEST_CASE("WiringTests_Partial_InputNamesOnly") { + + JApplication app; + + auto wiring_svc = app.GetService(); + toml::table table = toml::parse(facgen_partial_input_names_only); + wiring_svc->ApplyWiringSet(wiring_svc->ParseWiringSet(table)); + + auto gen = new JFactoryGeneratorT; + app.Add(gen); + app.Initialize(); + + auto event = std::make_shared(&app); + std::vector test_data; + test_data.push_back(new Cluster{0, 0, 22.2}); + event->Insert(test_data, "overridden_input"); + + auto results = event->Get("default_clusters"); + REQUIRE(results.size() == 1); + REQUIRE(results.at(0)->E == 23.2); + +} +static constexpr std::string_view facgen_partial_output_names_only = R"( + [[wiring]] + action = "update" + type_name = "WiredFac" + prefix = "wiredfac" + output_names = ["overridden_output"] +)"; + +TEST_CASE("WiringTests_Partial_OutputNamesOnly") { + + JApplication app; + + auto wiring_svc = app.GetService(); + toml::table table = toml::parse(facgen_partial_output_names_only); + wiring_svc->ApplyWiringSet(wiring_svc->ParseWiringSet(table)); + + auto gen = new JFactoryGeneratorT; + app.Add(gen); + app.Initialize(); + + auto event = std::make_shared(&app); + std::vector test_data; + test_data.push_back(new Cluster{0, 0, 22.2}); + event->Insert(test_data, "default_protos"); + + auto results = event->Get("overridden_output"); + REQUIRE(results.size() == 1); + REQUIRE(results.at(0)->E == 23.2); + +}