Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ROOT-based event exporter #900

Merged
merged 28 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3eff886
Draft dump to root option to HepMC3PrimaryGenerator.cc
stognini Aug 10, 2023
a591721
Move functionality to a separate class
stognini Aug 10, 2023
6a919c6
Change to per-event tree entries
stognini Aug 11, 2023
c7039fa
Add HepMC3RootReader; WIP on other fronts
stognini Aug 11, 2023
389d5cb
Update HepMC3RootReader to return events
stognini Aug 11, 2023
9fdd0c5
Reorg writer/reader
stognini Aug 15, 2023
b514630
Complete reader
stognini Aug 15, 2023
58a32be
Add UPRootAutoSave custom deleter; Clean up code
stognini Aug 17, 2023
5365993
Add offloadRootOutputFile macro cmd option
stognini Aug 17, 2023
bc4d486
Merge branch 'develop' into dump-primaries
stognini Aug 17, 2023
d822921
Address comments
stognini Aug 18, 2023
c14d2fb
Use template specialization for UPRootWritable<TTree>
stognini Aug 18, 2023
b0e9f43
Remove mutex from writer/reader
stognini Aug 18, 2023
8db8f19
Fix types
stognini Aug 18, 2023
2ba1132
Merge branch 'develop' into dump-primaries
stognini Aug 18, 2023
2b68f96
Refactor event IO test for reusability
sethrj Aug 19, 2023
473b5c8
Add root IO test
sethrj Aug 19, 2023
95d6885
Prevent move/copy for event reader and writer
sethrj Aug 19, 2023
e0bd0ab
Fix missing symbols error
stognini Aug 19, 2023
5b5340e
Update root unique ptrs
stognini Aug 19, 2023
0bfede8
Improve reader; Add ScopedRootErrorHandler; Remove TrackId branch
stognini Aug 19, 2023
3ac3ced
Add scoped error handler; clean up
stognini Aug 21, 2023
9f99cff
Update RootEventWriter to store contiguous event ids
stognini Aug 21, 2023
798b407
Fixup
stognini Aug 21, 2023
47c7618
Fixup; Minor improvements
stognini Aug 21, 2023
ea48c6a
Merge branch 'develop' into dump-primaries
stognini Aug 22, 2023
b5fa3d9
Merge remote-tracking branch 'upstream/develop' into dump-primaries
sethrj Aug 23, 2023
1ec6e33
Revert EventReader; Address comments
stognini Aug 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/celer-g4/celer-g4.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
#include <G4UImanager.hh>
#include <G4Version.hh>

#include "accel/HepMC3PrimaryGenerator.hh"

#include "GlobalSetup.hh"

#if G4VERSION_NUMBER >= 1100
# include <G4RunManagerFactory.hh>
#else
Expand Down Expand Up @@ -80,7 +84,7 @@ void run(int argc, char** argv)
CELER_LOG(info) << "Run manager type: "
<< TypeDemangler<G4RunManager>{}(*run_manager);

// Make global setup commands available to UI
// Construct singleton, also making it available to UI
GlobalSetup::Instance();

G4UImanager* ui = G4UImanager::GetUIpointer();
Expand Down
6 changes: 6 additions & 0 deletions src/accel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ endif()
if(CELERITAS_USE_HepMC3)
list(APPEND PRIVATE_DEPS HepMC3::HepMC3)
list(APPEND SOURCES HepMC3PrimaryGenerator.cc)

if (CELERITAS_USE_ROOT)
list(APPEND PRIVATE_DEPS ROOT::Tree)
list(APPEND SOURCES detail/RootOffloadWriter.cc)
list(APPEND SOURCES detail/RootOffloadReader.cc)
endif()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be reverted since the ROOT code is no longer in accel.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stognini You might have missed this comment earlier?

endif()

#-----------------------------------------------------------------------------#
Expand Down
9 changes: 8 additions & 1 deletion src/accel/LocalTransporter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "SharedParams.hh"
#include "detail/HitManager.hh"
#include "detail/OffloadWriter.hh"
#include "detail/RootOffloadWriter.hh"

namespace celeritas
{
Expand All @@ -39,6 +40,7 @@ LocalTransporter::LocalTransporter(SetupOptions const& options,
: auto_flush_(options.max_num_tracks)
, max_steps_(options.max_steps)
, dump_primaries_{params.offload_writer()}
, dump_primaries_root_{params.root_offload_writer()}
, hit_manager_{params.hit_manager()}
{
CELER_VALIDATE(params,
Expand Down Expand Up @@ -151,9 +153,14 @@ void LocalTransporter::Flush()

if (dump_primaries_)
{
// Write offload particles if user requested
// Write offload particles to HepMC3 if user requested
(*dump_primaries_)(buffer_);
}
if (dump_primaries_root_)
{
// Write offload particles to ROOT if user requested
(*dump_primaries_root_)(buffer_);
}

// Abort cleanly for interrupt and user-defined signals
ScopedSignalHandler interrupted{SIGINT, SIGUSR2};
Expand Down
3 changes: 3 additions & 0 deletions src/accel/LocalTransporter.hh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace detail
{
class HitManager;
class OffloadWriter;
class RootOffloadWriter;
} // namespace detail

struct SetupOptions;
Expand Down Expand Up @@ -76,6 +77,7 @@ class LocalTransporter
private:
using SPHitManger = std::shared_ptr<detail::HitManager>;
using SPOffloadWriter = std::shared_ptr<detail::OffloadWriter>;
using SPRootOffloadWriter = std::shared_ptr<detail::RootOffloadWriter>;

struct HMFinalizer
{
Expand All @@ -95,6 +97,7 @@ class LocalTransporter

// Shared across threads to write flushed particles
SPOffloadWriter dump_primaries_;
SPRootOffloadWriter dump_primaries_root_;
// Shared pointer across threads, "finalize" called when clearing
InitializedValue<SPHitManger, HMFinalizer> hit_manager_;
};
Expand Down
2 changes: 2 additions & 0 deletions src/accel/SetupOptions.hh
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ struct SetupOptions
std::string physics_output_file;
//! Filename to dump a HepMC3 copy of offloaded tracks as events
std::string offload_output_file;
//! Filename to dump a ROOT copy of offloaded tracks as events
std::string offload_root_output_file;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this; we'll reuse offload_output_file and switch format based on the file extension.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this one?

Copy link
Member Author

@stognini stognini Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh crap. Thanks for noticing this. I checked out back a few files from a previous commit just to test a celer-g4 run and undid my fixes. I'll clean this up.

//!@}

//!@{
Expand Down
3 changes: 3 additions & 0 deletions src/accel/SetupOptionsMessenger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ SetupOptionsMessenger::SetupOptionsMessenger(SetupOptions* options)
add_cmd(&options->offload_output_file,
"offloadOutputFile",
"Filename for HepMC3 copy of offloaded tracks as events");
add_cmd(&options->offload_root_output_file,
"offloadRootOutputFile",
"Filename for ROOT copy of offloaded tracks as events");
add_cmd(&options->max_num_tracks,
"maxNumTracks",
"Number of track \"slots\" to be transported simultaneously");
Expand Down
7 changes: 7 additions & 0 deletions src/accel/SharedParams.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "SetupOptions.hh"
#include "detail/HitManager.hh"
#include "detail/OffloadWriter.hh"
#include "detail/RootOffloadWriter.hh"

namespace celeritas
{
Expand Down Expand Up @@ -173,6 +174,12 @@ SharedParams::SharedParams(SetupOptions const& options)
options.offload_output_file, params_->particle());
}

if (!options.offload_root_output_file.empty())
{
root_offload_writer_ = std::make_shared<detail::RootOffloadWriter>(
options.offload_root_output_file, params_->particle());
}

CELER_ENSURE(*this);
}

Expand Down
9 changes: 9 additions & 0 deletions src/accel/SharedParams.hh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace detail
{
class HitManager;
class OffloadWriter;
class RootOffloadWriter;
} // namespace detail
class CoreParams;
struct Primary;
Expand Down Expand Up @@ -87,13 +88,20 @@ class SharedParams

using SPHitManager = std::shared_ptr<detail::HitManager>;
using SPOffloadWriter = std::shared_ptr<detail::OffloadWriter>;
using SPRootOffloadWriter = std::shared_ptr<detail::RootOffloadWriter>;

//! Hit manager, to be used only by LocalTransporter
SPHitManager const& hit_manager() const { return hit_manager_; }

//! Optional offload writer, only for use by LocalTransporter
SPOffloadWriter const& offload_writer() const { return offload_writer_; }

//! Optional ROOT offload writer, only for use by LocalTransporter
SPRootOffloadWriter const& root_offload_writer() const
{
return root_offload_writer_;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete all this code; we should be using the same offload writer interface: either HepMC3 or ROOT, we don't need separate copies of both. I just wanted you to add the RootEvent{Reader,Writer} class and I was going to add in a separate PR the extra bit of std::functional and "make a different writer based on the output extension" code needed to hook it in.

//!@}

private:
Expand All @@ -105,6 +113,7 @@ class SharedParams
VecG4ParticleDef particles_;
std::string output_filename_;
SPOffloadWriter offload_writer_;
SPRootOffloadWriter root_offload_writer_;

//// HELPER FUNCTIONS ////

Expand Down
105 changes: 105 additions & 0 deletions src/accel/detail/RootOffloadReader.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file accel/detail/RootOffloadReader.cc
//---------------------------------------------------------------------------//
#include "RootOffloadReader.hh"

#include <TFile.h>
#include <TLeaf.h>
#include <TTree.h>

#include "corecel/io/Logger.hh"
#include "celeritas/phys/ParticleParams.hh"

namespace celeritas
{
namespace detail
{
//---------------------------------------------------------------------------//
/*!
* Construct with ROOT input filename.
*/
RootOffloadReader::RootOffloadReader(std::string const& filename,
SPConstParticles params)
: params_(std::move(params))
{
CELER_EXPECT(!filename.empty());
tfile_.reset(TFile::Open(filename.c_str(), "read"));
CELER_ASSERT(tfile_->IsOpen());
ttree_.reset(tfile_->Get<TTree>(tree_name()));
CELER_ASSERT(ttree_);

num_entries_ = ttree_->GetEntries();
CELER_ASSERT(num_entries_ > 0);
}

//---------------------------------------------------------------------------//
/*!
* Read single event from the primaries tree.
*/
auto RootOffloadReader::operator()() -> result_type
{
std::lock_guard scoped_lock{read_mutex_};

CELER_EXPECT(entry_count_ <= num_entries_);
ttree_->GetEntry(entry_count_);
auto const this_evt_id = ttree_->GetLeaf("event_id")->GetValue();

result_type primaries;
for (; entry_count_ < num_entries_; entry_count_++)
{
ttree_->GetEntry(entry_count_);
if (ttree_->GetLeaf("event_id")->GetValue() != this_evt_id)
{
break;
}

Primary primary;
primary.event_id = EventId{this->from_leaf<std::size_t>("event_id")};
primary.track_id = TrackId{this->from_leaf<std::size_t>("track_id")};
primary.particle_id
= params_->find(PDGNumber{this->from_leaf<int>("particle")});
primary.energy = units::MevEnergy{this->from_leaf<double>("energy")};
primary.time = this->from_leaf<double>("time");
primary.position = this->from_array_leaf("pos");
primary.direction = this->from_array_leaf("dir");
primaries.push_back(std::move(primary));
}

CELER_LOG_LOCAL(info) << "Read event " << this_evt_id << " with "
<< primaries.size() << " primaries";
return primaries;
}

//---------------------------------------------------------------------------//
/*!
* Helper function to fetch leaves.
*/
template<class T>
auto RootOffloadReader::from_leaf(char const* leaf_name) -> T
{
CELER_EXPECT(ttree_);
auto const leaf = ttree_->GetLeaf(leaf_name);
CELER_ASSERT(leaf);
return static_cast<T>(leaf->GetValue());
}

//---------------------------------------------------------------------------//
/*!
* Helper function to fetch leaves containing `std::array<double, 3>`.
*/
Real3 RootOffloadReader::from_array_leaf(char const* leaf_name)
{
CELER_EXPECT(ttree_);
auto const leaf = ttree_->GetLeaf(leaf_name);
CELER_ASSERT(leaf);
CELER_ASSERT(leaf->GetLen() == 3);
return {leaf->GetValue(0), leaf->GetValue(1), leaf->GetValue(2)};
}

//---------------------------------------------------------------------------//
} // namespace detail
} // namespace celeritas
111 changes: 111 additions & 0 deletions src/accel/detail/RootOffloadReader.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file accel/detail/RootOffloadReader.hh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be celeritas/io/EventReader since we will need to use it from the celer-sim app.

//---------------------------------------------------------------------------//
#pragma once

#include <string>

#include "corecel/Macros.hh"
#include "celeritas/ext/RootUniquePtr.hh"
#include "celeritas/phys/Primary.hh"

// ROOT forward declaration
class TFile;
class TTree;

namespace celeritas
{
class ParticleParams;

namespace detail
{
//---------------------------------------------------------------------------//
/*!
* Read ROOT file generated by \c RootOffloadWriter .
*
* Use \c operator() to read new primaries:
* \code
RootOffloadReader read("primaries.root", particle_params);
auto event = read();
while (!event.empty())
{
event.read();
}
* \endcode
*/
class RootOffloadReader
{
public:
//!@{
//! \name Type aliases
using SPConstParticles = std::shared_ptr<ParticleParams const>;
using result_type = std::vector<Primary>;
//!@}

// Construct with ROOT filename
explicit RootOffloadReader(std::string const& filename,
SPConstParticles params);

// Read a single event from the ROOT file
result_type operator()();

private:
//// DATA ////

SPConstParticles params_;
std::size_t entry_count_{0}; // Current TTree entry
std::size_t num_entries_; // Total number of entries in the TTree
UPExtern<TFile> tfile_;
UPExtern<TTree> ttree_;
std::mutex read_mutex_;

//// HELPER FUNCTIONS ////

// Hardcoded ROOT TTree name defined by RootOffloadWriter
char const* tree_name() { return "primaries"; }

// Fetch basic data types from leaves
template<class T>
auto from_leaf(char const* leaf_name) -> T;

// Fetch arrays from leaves
Real3 from_array_leaf(char const* leaf_name);
};

//---------------------------------------------------------------------------//
#if !CELERITAS_USE_ROOT
inline RootOffloadReader::RootOffloadReader(std::string const&)
{
(void)sizeof(num_entries_);
(void)sizeof(entry_count_);
(void)sizeof(tfile_);
(void)sizeof(ttree_);
(void)sizeof(read_mutex_);
(void)sizeof(event_);
CELER_NOT_CONFIGURED("ROOT");
}

inline RootOffloadReader::result_type RootOffloadReader::operator()()
{
CELER_ASSERT_UNREACHABLE();
}

inline template<class T>
auto RootOffloadReader::from_leaf(char const*) -> T
{
CELER_ASSERT_UNREACHABLE();
}

Real3 RootOffloadReader::from_leaf(char const*)
{
CELER_ASSERT_UNREACHABLE();
}
#endif

//---------------------------------------------------------------------------//
} // namespace detail
} // namespace celeritas
Loading