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

Refactoring: Storage of Podio data #365

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f4bacda
Implement JCollection, JBasicCollection, JPodioCollection
nathanwbrei May 6, 2024
afa5e9a
JFactory, JFactorySet, JEvent can store and retrieve JCollections
nathanwbrei Aug 23, 2024
4c9e502
Adjust JFactory interface to support JCollections
nathanwbrei Aug 23, 2024
ebd6490
JFactory: Add PodioOutput, VariadicPodioOutput
nathanwbrei Aug 24, 2024
3df7ddc
Test cases pass
nathanwbrei Aug 24, 2024
afc8f89
JEvent::InsertCollection uses JCollection
nathanwbrei Aug 25, 2024
940b231
JFactoryPodioT uses JPodioCollection
nathanwbrei Sep 3, 2024
738374e
Rename JCollection to JStorage
nathanwbrei Sep 6, 2024
c8136a2
JPodioStorage test cases
nathanwbrei Sep 6, 2024
580f702
More renaming
nathanwbrei Sep 6, 2024
e6662e0
Add BasicStorage and BasicOutput
nathanwbrei Sep 7, 2024
763a3fa
Fully replace JEvent podio machinery with JStorage equivalent
nathanwbrei Sep 19, 2024
b9c4e1d
JFactoryPodioT inherits from JFactory, not JFactoryT
nathanwbrei Sep 19, 2024
f255192
JMultifactory no longer uses JPodioFactoryT
nathanwbrei Sep 19, 2024
ee60443
Fix JFactorySet ownership confusion
nathanwbrei Sep 19, 2024
c93938a
JFactorySet no longer depends on JFactoryT
nathanwbrei Sep 19, 2024
c94ac6f
Only run StorageTests when USE_PODIO=1
nathanwbrei Sep 19, 2024
34088d0
Fix test case
nathanwbrei Sep 20, 2024
d29d6aa
Replace std::optional with JOptional
nathanwbrei Sep 20, 2024
2463f26
Clear PodioStorage correctly
nathanwbrei Sep 20, 2024
41ae312
Further JFactory::ClearData fixes
nathanwbrei Sep 21, 2024
4b5c0a7
Bring back JFactorySet::GetFactory<T>
nathanwbrei Sep 21, 2024
da316c7
Remove templating from JPodioStorage
nathanwbrei Sep 23, 2024
36ebc7e
Deep terminology cleanup
nathanwbrei Sep 23, 2024
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
5 changes: 0 additions & 5 deletions src/libraries/JANA/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ set(JANA2_SOURCES
Compatibility/md5.c
)

if (${USE_PODIO})
list(APPEND JANA2_SOURCES
Podio/JFactoryPodioT.cc
)
endif()

if (NOT ${USE_XERCES})
message(STATUS "Skipping support for libJANA's JGeometryXML because USE_XERCES=Off")
Expand Down
108 changes: 108 additions & 0 deletions src/libraries/JANA/Components/JBasicDataBundle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@

#pragma once

#include <JANA/Components/JDataBundle.h>
#include <JANA/JObject.h>
#include <JANA/Utils/JTypeInfo.h>

#ifdef JANA2_HAVE_ROOT
#include <TObject.h>
#endif

class JBasicDataBundle : public JDataBundle {
bool m_is_persistent = false;
bool m_not_object_owner = false;
bool m_write_to_output = true;

public:
JBasicDataBundle() = default;
~JBasicDataBundle() override = default;
void SetPersistentFlag(bool persistent) { m_is_persistent = persistent; }
void SetNotOwnerFlag(bool not_owner) { m_not_object_owner = not_owner; }
void SetWriteToOutputFlag(bool write_to_output) { m_write_to_output = write_to_output; }

bool GetPersistentFlag() { return m_is_persistent; }
bool GetNotOwnerFlag() { return m_not_object_owner; }
bool GetWriteToOutputFlag() { return m_write_to_output; }
};



template <typename T>
class JBasicDataBundleT : public JBasicDataBundle {
private:
std::vector<T*> m_data;

public:
JBasicDataBundleT();
void ClearData() override;
size_t GetSize() const override { return m_data.size();}

std::vector<T*>& GetData() { return m_data; }

/// EnableGetAs generates a vtable entry so that users may extract the
/// contents of this JFactoryT from the type-erased JFactory. The user has to manually specify which upcasts
/// to allow, and they have to do so for each instance. It is recommended to do so in the constructor.
/// Note that EnableGetAs<T>() is called automatically.
template <typename S> void EnableGetAs ();

// The following specializations allow automatically adding standard types (e.g. JObject) using things like
// std::is_convertible(). The std::true_type version defers to the standard EnableGetAs().
template <typename S> void EnableGetAs(std::true_type) { EnableGetAs<S>(); }
template <typename S> void EnableGetAs(std::false_type) {}
};

// Template definitions

template <typename T>
JBasicDataBundleT<T>::JBasicDataBundleT() {
SetTypeName(JTypeInfo::demangle<T>());
EnableGetAs<T>();
EnableGetAs<JObject>( std::is_convertible<T,JObject>() ); // Automatically add JObject if this can be converted to it
#ifdef JANA2_HAVE_ROOT
EnableGetAs<TObject>( std::is_convertible<T,TObject>() ); // Automatically add TObject if this can be converted to it
#endif
}

template <typename T>
void JBasicDataBundleT<T>::ClearData() {

// ClearData won't do anything if Init() hasn't been called
if (GetStatus() == Status::Empty) {
return;
}
// ClearData() does nothing if persistent flag is set.
// User must manually recycle data, e.g. during ChangeRun()
if (GetPersistentFlag()) {
return;
}

// Assuming we _are_ the object owner, delete the underlying jobjects
if (!GetNotOwnerFlag()) {
for (auto p : m_data) delete p;
}
m_data.clear();
SetStatus(Status::Empty);
}

template<typename T>
template<typename S>
void JBasicDataBundleT<T>::EnableGetAs() {

auto upcast_lambda = [this]() {
std::vector<S*> results;
for (auto t : m_data) {
results.push_back(static_cast<S*>(t));
}
return results;
};

auto key = std::type_index(typeid(S));
using upcast_fn_t = std::function<std::vector<S*>()>;
mUpcastVTable[key] = std::unique_ptr<JAny>(new JAnyT<upcast_fn_t>(std::move(upcast_lambda)));
}





27 changes: 27 additions & 0 deletions src/libraries/JANA/Components/JBasicOutput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once
#include <JANA/Components/JHasFactoryOutputs.h>
#include <JANA/JEvent.h>

namespace jana::components {

template <typename T>
class Output : public JHasFactoryOutputs::OutputBase {
std::vector<T*> m_data;

public:
Output(JHasFactoryOutputs* owner, std::string default_tag_name="") {
owner->RegisterOutput(this);
this->collection_names.push_back(default_tag_name);
this->type_name = JTypeInfo::demangle<T>();
}

std::vector<T*>& operator()() { return m_data; }

protected:
void PutCollections(const JEvent& event) override {
event.Insert(m_data, this->collection_names[0]);
}
void Reset() override { }
};

} // jana::components
102 changes: 102 additions & 0 deletions src/libraries/JANA/Components/JDataBundle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2024, Jefferson Science Associates, LLC.
// Subject to the terms in the LICENSE file found in the top-level directory.

#pragma once

#include <JANA/JException.h>
#include <JANA/Utils/JAny.h>
#include <JANA/Utils/JCallGraphRecorder.h>

#include <string>
#include <functional>
#include <typeindex>
#include <vector>
#include <memory>
#include <unordered_map>


class JFactory;

class JDataBundle {
public:
// Typedefs
enum class Status { Empty, Created, Inserted, InsertedViaGetObjects };

private:
// Fields
Status m_status = Status::Empty;
std::string m_unique_name;
JOptional<std::string> m_short_name;
std::string m_type_name;
JFactory* m_factory = nullptr;
JOptional<std::type_index> m_inner_type_index;
mutable JCallGraphRecorder::JDataOrigin m_insert_origin = JCallGraphRecorder::ORIGIN_NOT_AVAILABLE;

protected:
std::unordered_map<std::type_index, std::unique_ptr<JAny>> mUpcastVTable;

public:
// Interface
JDataBundle() = default;
virtual ~JDataBundle() = default;
virtual size_t GetSize() const = 0;
virtual void ClearData() = 0;

// Getters
Status GetStatus() const { return m_status; }
std::string GetUniqueName() const { return m_unique_name; }
JOptional<std::string> GetShortName() const { return m_short_name; }
std::string GetTypeName() const { return m_type_name; }
JOptional<std::type_index> GetTypeIndex() const { return m_inner_type_index; }
JCallGraphRecorder::JDataOrigin GetInsertOrigin() const { return m_insert_origin; } ///< If objects were placed here by JEvent::Insert() this records whether that call was made from a source or factory.
JFactory* GetFactory() const { return m_factory; }

// Setters
void SetStatus(Status s) { m_status = s;}
void SetUniqueName(std::string unique_name) { m_unique_name = unique_name; }
void SetShortName(std::string short_name) { m_short_name = short_name; }
void SetTypeName(std::string type_name) { m_type_name = type_name; }
void SetInsertOrigin(JCallGraphRecorder::JDataOrigin origin) { m_insert_origin = origin; } ///< Called automatically by JEvent::Insert() to records whether that call was made by a source or factory.
void SetFactory(JFactory* fac) { m_factory = fac; }

// Templates
//
/// Generically access the encapsulated data, performing an upcast if necessary. This is useful for extracting data from
/// all JFactories<T> where T extends a parent class S, such as JObject or TObject, in contexts where T is not known
/// or it would introduce an unwanted coupling. The main application is for building DSTs.
///
/// Be aware of the following caveats:
/// - The factory's object type must not use virtual inheritance.
/// - If JFactory::Process hasn't already been called, this will return an empty vector. This will NOT call JFactory::Process.
/// - Someone must call JFactoryT<T>::EnableGetAs<S>, preferably the constructor. Otherwise, this will return an empty vector.
/// - If S isn't a base class of T, this will return an empty vector.
template<typename S>
std::vector<S*> GetAs();

};



// Because C++ doesn't support templated virtual functions, we implement our own dispatch table, mUpcastVTable.
// This means that the JFactoryT is forced to manually populate this table by calling JFactoryT<T>::EnableGetAs.
// We have the option to make the vtable be a static member of JFactoryT<T>, but we have chosen not to because:
//
// 1. It would be inconsistent with the fact that the user is supposed to call EnableGetAs in the ctor
// 2. People in the future may want to generalize GetAs to support user-defined S* -> T* conversions (which I don't recommend)
// 3. The size of the vtable is expected to be very small (<10 elements, most likely 2)

template<typename S>
std::vector<S*> JDataBundle::GetAs() {
std::vector<S*> results;
auto ti = std::type_index(typeid(S));
auto search = mUpcastVTable.find(ti);
if (search != mUpcastVTable.end()) {
using upcast_fn_t = std::function<std::vector<S*>()>;
auto temp = static_cast<JAnyT<upcast_fn_t>*>(&(*search->second));
upcast_fn_t upcast_fn = temp->t;
results = upcast_fn();
}
return results;
}


40 changes: 40 additions & 0 deletions src/libraries/JANA/Components/JHasFactoryOutputs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

#pragma once
#include <JANA/Components/JDataBundle.h>
#include <memory>

class JEvent;

namespace jana::components {


class JHasFactoryOutputs {
public:
struct OutputBase {
protected:
std::vector<std::unique_ptr<JDataBundle>> m_databundles;
bool m_is_variadic = false;
public:
const std::vector<std::unique_ptr<JDataBundle>>& GetDataBundles() const { return m_databundles; }
virtual void StoreData(const JEvent& event) = 0;
virtual void Reset() = 0;
};

private:
std::vector<OutputBase*> m_outputs;

public:
const std::vector<OutputBase*>& GetOutputs() const {
return m_outputs;
}

void RegisterOutput(OutputBase* output) {
m_outputs.push_back(output);
}
};



} // namespace jana::components


42 changes: 42 additions & 0 deletions src/libraries/JANA/Components/JPodioDataBundle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024, Jefferson Science Associates, LLC.
// Subject to the terms in the LICENSE file found in the top-level directory.

#pragma once

#include <JANA/Components/JDataBundle.h>
#include <podio/CollectionBase.h>
#include <podio/podioVersion.h>


class JPodioDataBundle : public JDataBundle {

private:
const podio::CollectionBase* m_collection = nullptr;

public:
size_t GetSize() const override {
if (m_collection == nullptr) {
return 0;
}
return m_collection->size();
}

virtual void ClearData() override {
m_collection = nullptr;
SetStatus(JDataBundle::Status::Empty);
// Podio clears the data itself when the frame is destroyed.
// Until then, the collection is immutable.
//
// Consider: Instead of putting the frame in its own JFactory, maybe we
// want to maintain a shared_ptr to the frame here, and delete the
// the reference on ClearData(). Thus, the final call to ClearData()
// for each events deletes the frame and actually frees the data.
// This would let us support multiple frames within one event, though
// it might also prevent the user from accessing frames directly.
}

const podio::CollectionBase* GetCollection() const { return m_collection; }
void SetCollection(const podio::CollectionBase* collection) { m_collection = collection; }
};


Loading
Loading