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

Use Gaudi::Functional, add examples and adapt PodioInput to use Gaudi::Functional #129

Merged
merged 68 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
dbc4808
Add example producer
jmcarcell Sep 4, 2023
dbfe999
Up
jmcarcell Jul 9, 2023
3c40628
Up
jmcarcell Jul 9, 2023
f74c865
Up
jmcarcell Jul 9, 2023
28a35c1
Update the example producer
jmcarcell Jul 26, 2023
bdbcf73
Change to Functional Producer
jmcarcell Jul 26, 2023
ae4f679
Add a consumer, producer and transformer
jmcarcell Jul 27, 2023
3d88081
Remove deleted file
jmcarcell Jul 27, 2023
26e08e8
Add some information in the README about Gaudi::Functional
jmcarcell Jul 27, 2023
b2b50da
Add the functional code and tests
jmcarcell Sep 4, 2023
40a9af3
Add comments and cleanup to the functional algorithms
jmcarcell Jul 27, 2023
a03296a
Rename some some files
jmcarcell Jul 27, 2023
66b454e
Rename to ExampleFunctional
jmcarcell Jul 27, 2023
a3d616d
Update the option files and a producer with multiple outputs
jmcarcell Jul 27, 2023
a9b050f
Rename also in CMakeLists.txt
jmcarcell Sep 4, 2023
2db8c79
Add missing include
jmcarcell Jul 27, 2023
a8ef480
Add more information in the README
jmcarcell Jul 27, 2023
673044a
Fix ExampleFunctionalTransformer
jmcarcell Jul 27, 2023
30578ab
Add a constructor for unique_ptrs and change getData to const
jmcarcell Jul 27, 2023
6f8910e
Move PodioInput to use Gaudi::Functional
jmcarcell Jul 27, 2023
5d7fa0c
Allow not being the owner in DataWrapper.h
jmcarcell Jul 27, 2023
4ce096c
Fix some collection names
jmcarcell Jul 27, 2023
31e5855
Rename the collections
jmcarcell Jul 27, 2023
38cb3b1
Remove cout
jmcarcell Jul 27, 2023
ac42a85
Change to dynamic_cast
jmcarcell Jul 27, 2023
06b611d
Improve slightly the interface for reading
jmcarcell Jul 27, 2023
1fc6d10
Rename a couple of variables
jmcarcell Jul 27, 2023
45a2080
Add a Consumer with multiple inputs
jmcarcell Jul 27, 2023
f703cc8
Rename output keys
jmcarcell Jul 27, 2023
4861d11
Fix the ExampleFunctionalTransformer test
jmcarcell Aug 1, 2023
69d1bf2
Improve the FunctionalConsumerMultiple test
jmcarcell Aug 1, 2023
a7235df
Change from asserts to ifs
jmcarcell Aug 1, 2023
f462c8c
Add a functional test from many to many
jmcarcell Aug 1, 2023
3774a90
Rename handlers
jmcarcell Aug 1, 2023
acf7d53
Add an options file to run the TransformerMultiple
jmcarcell Aug 1, 2023
26d8281
Fix tests in parallel and fix the transformer multiple test
jmcarcell Aug 1, 2023
e86829d
Improve documentation and rename the input and output collections
jmcarcell Aug 1, 2023
fe718e7
Run clang-format
jmcarcell Aug 2, 2023
d6d0331
Run clang-format
jmcarcell Aug 2, 2023
64df3c1
Run clang-format
jmcarcell Aug 2, 2023
65a2573
Add root files to the gitignore
jmcarcell Sep 4, 2023
545a75b
Fix gitignore
jmcarcell Aug 2, 2023
80f48c8
Improve the interface for producers
jmcarcell Aug 8, 2023
638f7e5
Fix name
jmcarcell Aug 19, 2023
cb6139f
Fix the producers
jmcarcell Aug 21, 2023
57f7c2b
Fix tests
jmcarcell Aug 21, 2023
da2107e
Fix the consumer tests
jmcarcell Aug 22, 2023
5294aab
Fix tests
jmcarcell Sep 4, 2023
51890e0
Add registering template, simplify input interfaces for functional algs
jmcarcell Sep 10, 2023
f58bc21
Rename FunctionalUtils.h to BaseClass.h
jmcarcell Sep 13, 2023
5b0d022
Don't include the datawrappers
jmcarcell Sep 14, 2023
9e79c83
Reorganize imports
jmcarcell Sep 19, 2023
8ae87a3
Remove CollName from the arguments of maybeRead
jmcarcell Sep 20, 2023
b5840e8
Produce an empty MCParticleCollection in the exampleFunctionalProduce…
jmcarcell Sep 20, 2023
89b29ee
Fix a maybeRead call since type was removed in a previous commit
jmcarcell Sep 20, 2023
f5a5040
Fix the tests after the producer multiple outputs two MCParticleColle…
jmcarcell Sep 20, 2023
f185ca3
Add a comment explaining a test dependency
jmcarcell Sep 20, 2023
4e3ee36
Remove the podio/CollectionBase.h include
jmcarcell Sep 20, 2023
cbf9987
Adress comments from PR
jmcarcell Sep 30, 2023
716c5c4
Add header explaining each functional test
jmcarcell Oct 1, 2023
6849e50
Add tests chaining functional algorithms and mixing them with the old…
jmcarcell Oct 1, 2023
8baed4b
Remove some unneeded includes
jmcarcell Oct 3, 2023
39d560c
Fix the wrong name of a test
jmcarcell Oct 3, 2023
b80a6cb
Run clang-format
jmcarcell Oct 3, 2023
bda0c3c
Fix licenses
jmcarcell Oct 3, 2023
86750a4
Fix remaining licenses
jmcarcell Oct 3, 2023
550d7c8
Fix typedef style
jmcarcell Oct 4, 2023
4d03256
Emphasize message in the README
jmcarcell Oct 4, 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
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
### File copied from https://github.com/key4hep/key4hep-dev-utils
### DO NOT EDIT, CHANGES WILL BE OVERWRITTEN

### C++ ###
# Prerequisites
*.d
Expand Down Expand Up @@ -241,7 +238,7 @@ podio_generated_files.cmake
/python/edm4hep/__version__.py
edm4hep/edm4hep/
edm4hep/src/
## k4FWCore
# k4FWCore
test/k4FWCoreTest/**/*.root
## k4MarlinWrapper
test/inputFiles/*.slcio
Expand Down
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,35 @@ print(my_opts[0].foo)

k4FWCore is a CMake project. After setting up the dependencies (use for example `source /cvmfs/sw.hsf.org/key4hep/setup.sh`)


```
mkdir build install
cd build;
``` bash
mkdir build
cd build
cmake ..
make install
```


## Implementing algorithms
k4FWCore uses `Gaudi::Functional` for executing algorithms. There are several
types of algorithms, depending on your use case:
- The `Consumer` takes inputs but no outputs; can be used for reading
- The `Producer` takes outputs but no inputs; can be used for generating
collections or events
- The `Transformer` is the more general one (both the `Consumer` and the
`Producer` are a particular case of this one) and takes both inputs and
outputs

A more complete list of algorithms can be found in
https://lhcb.github.io/DevelopKit/03a-gaudi/, in the `Gaudi::Functional`
section.

In all cases the implementation process is the same: we'll create a new class
that will inherit from one of the previous algorithms. Then, we implement
`operator()`, where our algorithm will be. This `operator()` will return either
a single type (including `void`) or a tuple with multiple types. It will take
one parameter per input. Simple examples can be found in the test folder for
each one of the above-mentioned algorithms. In addition, there are tests that
have either multiple inputs and / or multiple outputs (like
`ExampleFunctionalProducerMultiple`) that can be used as a template for the more
typical case when working with multiple inputs or outputs.

`GaudiAlg` is deprecated and will be removed in future versions of Gaudi.
Copy link
Contributor

Choose a reason for hiding this comment

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

emphasize it visually

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean add an example here? Or at least the structure with the class definition and template arguments and operator()?

186 changes: 159 additions & 27 deletions k4FWCore/components/PodioInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,181 @@
* limitations under the License.
*/
#include "PodioInput.h"
#include "GaudiAlg/Consumer.h"

#include "TFile.h"
#include "TROOT.h"

#include "k4FWCore/DataWrapper.h"
#include "k4FWCore/PodioDataSvc.h"

#include "edm4hep/CaloHitContributionCollection.h"
#include "edm4hep/CalorimeterHitCollection.h"
#include "edm4hep/ClusterCollection.h"
#include "edm4hep/MCParticleCollection.h"
#include "edm4hep/MCRecoCaloAssociationCollection.h"
#include "edm4hep/MCRecoClusterParticleAssociationCollection.h"
#include "edm4hep/MCRecoParticleAssociationCollection.h"
#include "edm4hep/MCRecoTrackParticleAssociationCollection.h"
#include "edm4hep/MCRecoTrackerAssociationCollection.h"
#include "edm4hep/MCRecoTrackerHitPlaneAssociationCollection.h"
#include "edm4hep/ParticleIDCollection.h"
#include "edm4hep/RawCalorimeterHitCollection.h"
#include "edm4hep/RawTimeSeriesCollection.h"
#include "edm4hep/RecDqdxCollection.h"
#include "edm4hep/RecIonizationClusterCollection.h"
#include "edm4hep/RecoParticleVertexAssociationCollection.h"
#include "edm4hep/ReconstructedParticleCollection.h"
#include "edm4hep/SimCalorimeterHitCollection.h"
#include "edm4hep/SimPrimaryIonizationClusterCollection.h"
#include "edm4hep/SimTrackerHitCollection.h"
#include "edm4hep/TimeSeriesCollection.h"
#include "edm4hep/TrackCollection.h"
#include "edm4hep/TrackerHitCollection.h"
#include "edm4hep/TrackerHitPlaneCollection.h"
#include "edm4hep/TrackerPulseCollection.h"
#include "edm4hep/VertexCollection.h"

#include "podio/UserDataCollection.h"

DECLARE_COMPONENT(PodioInput)

PodioInput::PodioInput(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {}
template <typename T> inline void PodioInput::maybeRead(std::string_view collName) const {
if (m_podioDataSvc->readCollection<T>(std::string(collName)).isFailure()) {
error() << "Failed to register collection " << collName << endmsg;
}
}

StatusCode PodioInput::initialize() {
if (GaudiAlgorithm::initialize().isFailure())
return StatusCode::FAILURE;
void PodioInput::fillReaders() {
m_readers["edm4hep::MCParticleCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCParticleCollection>(collName);
};
m_readers["edm4hep::SimTrackerHitCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::SimTrackerHitCollection>(collName);
};
m_readers["edm4hep::CaloHitContributionCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::CaloHitContributionCollection>(collName);
};
m_readers["edm4hep::SimCalorimeterHitCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::SimCalorimeterHitCollection>(collName);
};
m_readers["edm4hep::RawCalorimeterHitCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::RawCalorimeterHitCollection>(collName);
};
m_readers["edm4hep::CalorimeterHitCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::CalorimeterHitCollection>(collName);
};
m_readers["edm4hep::ParticleIDCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::ParticleIDCollection>(collName);
};
m_readers["edm4hep::ClusterCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::ClusterCollection>(collName);
};
m_readers["edm4hep::TrackerHitCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::TrackerHitCollection>(collName);
};
m_readers["edm4hep::TrackerHitPlaneCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::TrackerHitPlaneCollection>(collName);
};
m_readers["edm4hep::RawTimeSeriesCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::RawTimeSeriesCollection>(collName);
};
m_readers["edm4hep::TrackCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::TrackCollection>(collName);
};
m_readers["edm4hep::VertexCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::VertexCollection>(collName);
};
m_readers["edm4hep::ReconstructedParticleCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::ReconstructedParticleCollection>(collName);
};
m_readers["edm4hep::MCRecoParticleAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCRecoParticleAssociationCollection>(collName);
};
m_readers["edm4hep::MCRecoCaloAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCRecoCaloAssociationCollection>(collName);
};
m_readers["edm4hep::MCRecoTrackerAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCRecoTrackerAssociationCollection>(collName);
};
m_readers["edm4hep::MCRecoTrackerHitPlaneAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCRecoTrackerHitPlaneAssociationCollection>(collName);
};
m_readers["edm4hep::MCRecoClusterParticleAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCRecoClusterParticleAssociationCollection>(collName);
};
m_readers["edm4hep::MCRecoTrackParticleAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::MCRecoTrackParticleAssociationCollection>(collName);
};
m_readers["edm4hep::RecoParticleVertexAssociationCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::RecoParticleVertexAssociationCollection>(collName);
};
m_readers["edm4hep::SimPrimaryIonizationClusterCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::SimPrimaryIonizationClusterCollection>(collName);
};
m_readers["edm4hep::TrackerPulseCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::TrackerPulseCollection>(collName);
};
m_readers["edm4hep::RecIonizationClusterCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::RecIonizationClusterCollection>(collName);
};
m_readers["edm4hep::TimeSeriesCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::TimeSeriesCollection>(collName);
};
m_readers["edm4hep::RecDqdxCollection"] = [&](std::string_view collName) {
maybeRead<edm4hep::RecDqdxCollection>(collName);
};
m_readers["podio::UserDataCollection<int>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<int>>(collName);
};
m_readers["podio::UserDataCollection<float>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<float>>(collName);
};
m_readers["podio::UserDataCollection<double>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<double>>(collName);
};
m_readers["podio::UserDataCollection<int8_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<int8_t>>(collName);
};
m_readers["podio::UserDataCollection<int16_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<int16_t>>(collName);
};
m_readers["podio::UserDataCollection<int32_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<int32_t>>(collName);
};
m_readers["podio::UserDataCollection<int64_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<int64_t>>(collName);
};
m_readers["podio::UserDataCollection<uint8_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<uint8_t>>(collName);
};
m_readers["podio::UserDataCollection<uint16_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<uint16_t>>(collName);
};
m_readers["podio::UserDataCollection<uint32_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<uint32_t>>(collName);
};
m_readers["podio::UserDataCollection<uint64_t>"] = [&](std::string_view collName) {
maybeRead<podio::UserDataCollection<uint64_t>>(collName);
};
}

PodioInput::PodioInput(const std::string& name, ISvcLocator* svcLoc) : Consumer(name, svcLoc) {
// check whether we have the PodioEvtSvc active
m_podioDataSvc = dynamic_cast<PodioDataSvc*>(evtSvc().get());
if (nullptr == m_podioDataSvc)
return StatusCode::FAILURE;

// TODO: add an upfront check for existence of data products

return StatusCode::SUCCESS;
if (!m_podioDataSvc) {
error() << "Could not get PodioDataSvc" << endmsg;
}
fillReaders();
}

StatusCode PodioInput::execute() {
size_t cntr = 0;
// Re-create the collections from ROOT file

void PodioInput::operator()() const {
for (auto& collName : m_collectionNames) {
debug() << "Registering collection to read " << collName << endmsg;
if (m_podioDataSvc->readCollection(collName).isFailure()) {
return StatusCode::FAILURE;
auto type = m_podioDataSvc->getCollectionType(collName);
if (m_readers.find(type) != m_readers.end()) {
m_readers[type](collName);
} else {
maybeRead<podio::CollectionBase>(collName);
}
}

// Tell data service that we are done with requested collections
m_podioDataSvc->endOfRead();
return StatusCode::SUCCESS;
}

StatusCode PodioInput::finalize() {
if (GaudiAlgorithm::finalize().isFailure())
return StatusCode::FAILURE;
return StatusCode::SUCCESS;
}
28 changes: 13 additions & 15 deletions k4FWCore/components/PodioInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@
*/
#ifndef FWCORE_PODIOINPUT_H
#define FWCORE_PODIOINPUT_H
// Gaaudi
#include "GaudiAlg/GaudiAlgorithm.h"
// Gaudi
#include "Gaudi/Property.h"
#include "GaudiAlg/Consumer.h"

// STL
#include <string>
#include <vector>

// forward declarations
// from k4FWCore:
class PodioDataSvc;

/** @class PodioInput
Expand All @@ -36,22 +35,21 @@ class PodioDataSvc;
* @author J. Lingemann
*/

class PodioInput : public GaudiAlgorithm {
using BaseClass_t = Gaudi::Functional::Traits::BaseClass_t<Gaudi::Algorithm>;

class PodioInput final : public Gaudi::Functional::Consumer<void(), BaseClass_t> {
public:
/// Constructor.
PodioInput(const std::string& name, ISvcLocator* svcLoc);
/// Initialization of PodioInput. Acquires the data service, opens root file and creates trees.
virtual StatusCode initialize();
/// Execute. Re-creates collections that are specified to be read and sets references.
virtual StatusCode execute();
/// Finalize. Closes ROOT file.
virtual StatusCode finalize();
void operator()() const override;

private:
/// Name of collections to read. Set by option collections (this is temporary)
template <typename T> void maybeRead(std::string_view collName) const;
void fillReaders();
// Name of collections to read. Set by option collections (this is temporary)
Gaudi::Property<std::vector<std::string>> m_collectionNames{this, "collections", {}, "Places of collections to read"};
/// Data service: needed to register objects and get collection IDs. Just an observing pointer.
PodioDataSvc* m_podioDataSvc;
// Data service: needed to register objects and get collection IDs. Just an observing pointer.
PodioDataSvc* m_podioDataSvc;
mutable std::map<std::string_view, std::function<void(std::string_view)>> m_readers;
};

#endif
36 changes: 36 additions & 0 deletions k4FWCore/include/k4FWCore/BaseClass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2014-2023 Key4hep-Project.
*
* This file is part of Key4hep.
* See https://key4hep.github.io/key4hep-doc/ for further info.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef K4FWCORE_FUNCTIONALUTILS_H
#define K4FWCORE_FUNCTIONALUTILS_H

#include "GaudiAlg/GaudiAlgorithm.h"
#include "GaudiKernel/DataObjectHandle.h"
#include "k4FWCore/DataWrapper.h"

// Base class used for the Traits template argument of the
// Gaudi::Functional algorithms
struct BaseClass_t {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a badly chosen name - and not even name spaced. Do you have something more prescriptive what the purpose is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here the idea is that the Gaudi examples always use a BaseClass_t that is defined to be typically Gaudi::Algorithm so by having this the changes are minimal. See for example https://gitlab.cern.ch/gaudi/Gaudi/-/blob/master/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp#L60 and a few lines below; In k4FWCore we don't need line 60 but we need instead #include "BaseClass.h" and the rest is exactly the same as in the Gaudi examples. We could also name it differently and have it namespaced but I thought for consistency it would be simpler this way.

template <typename T> using InputHandle = DataObjectReadHandle<DataWrapper<T>>;
template <typename T> using OutputHandle = DataObjectWriteHandle<DataWrapper<T>>;

using BaseClass = Gaudi::Algorithm;
};

#endif
15 changes: 13 additions & 2 deletions k4FWCore/include/k4FWCore/DataWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,33 @@ template <class T> class GAUDI_API DataWrapper : public DataWrapperBase {

public:
DataWrapper() : m_data(nullptr){};
DataWrapper(T&& coll) {
m_data = new T(std::move(coll));
is_owner = true;
}
DataWrapper(std::unique_ptr<T> uptr) : m_data(uptr.get()) {
uptr.release();
is_owner = false;
};
virtual ~DataWrapper();

const T* getData() { return m_data; }
const T* getData() const { return m_data; }
void setData(const T* data) { m_data = data; }
virtual void resetData() { m_data = nullptr; }

operator const T&() const& { return *m_data; }

private:
/// try to cast to collectionBase; may return nullptr;
virtual podio::CollectionBase* collectionBase();

private:
const T* m_data;
bool is_owner{true};
};

template <class T> DataWrapper<T>::~DataWrapper() {
if (m_data != nullptr)
if (is_owner && !m_data)
delete m_data;
}

Expand Down
Loading
Loading