Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions Detectors/EMCAL/workflow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ o2_add_library(EMCALWorkflow
src/EntropyEncoderSpec.cxx
src/EntropyDecoderSpec.cxx
src/StandaloneAODProducerSpec.cxx
src/EventBuilderSpec.cxx
PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsEMCAL O2::EMCALSimulation O2::Steer
O2::DPLUtils O2::EMCALBase O2::EMCALReconstruction O2::Algorithm O2::MathUtils)

Expand All @@ -30,6 +31,16 @@ o2_add_executable(reco-workflow
SOURCES src/emc-reco-workflow.cxx
PUBLIC_LINK_LIBRARIES O2::EMCALWorkflow)

o2_add_executable(multi-rawfitter-workflow
COMPONENT_NAME emcal
SOURCES src/multi-rawfitter-workflow.cxx
PUBLIC_LINK_LIBRARIES O2::EMCALWorkflow)

o2_add_executable(event-builder-workflow
COMPONENT_NAME emcal
SOURCES src/event-builder-workflow.cxx
PUBLIC_LINK_LIBRARIES O2::EMCALWorkflow)

o2_add_executable(entropy-encoder-workflow
COMPONENT_NAME emcal
SOURCES src/entropy-encoder-workflow.cxx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

#include <cstdint>
#include <set>
#include <vector>

#include "CommonDataFormat/RangeReference.h"
#include "CommonDataFormat/InteractionRecord.h"
#include "Framework/DataProcessorSpec.h"
#include "Framework/Task.h"
#include "Headers/DataHeader.h"

namespace o2
{

namespace emcal
{
class TriggerRecord;

namespace reco_workflow
{

/// \class EventBuilderSpec
/// \brief Class combining cell data from different subtimeframes into single cell range
/// \ingroup EMCALEMCALworkflow
/// \author Markus Fasel <markus.fasek@cern.ch>, Oak Ridge National Laboratory
/// \since Aug 9, 2021
///
/// The data processor combines cells from the same trigger in different subtimeframes
/// into a single cell container. Cells within the event range are ordered according
/// to the tower ID. New trigger record objects indicate the combined.
class EventBuilderSpec : public framework::Task
{
public:
/// \brief Constructor
EventBuilderSpec() = default;

/// \brief Destructor
~EventBuilderSpec() override = default;

void init(framework::InitContext& ctx) final;
void run(framework::ProcessingContext& ctx) final;

private:
struct RangeSubspec {
header::DataHeader::SubSpecificationType mSpecification;
dataformats::RangeReference<int, int> mDataRage;
};

struct RangeCollection {
InteractionRecord mInteractionRecord;
uint32_t mTriggerType;
std::vector<RangeSubspec> mRangesSubtimeframe;

bool operator==(const RangeCollection& other) const { return mInteractionRecord == other.mInteractionRecord; }
bool operator<(const RangeCollection& other) const { return mInteractionRecord < other.mInteractionRecord; }
};
std::set<RangeCollection> connectRangesFromSubtimeframes(const std::unordered_map<header::DataHeader::SubSpecificationType, gsl::span<const TriggerRecord>>& triggerrecords) const;
};

o2::framework::DataProcessorSpec getEventBuilderSpec();

} // namespace reco_workflow

} // namespace emcal

} // namespace o2
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Framework/Task.h"
#include "DataFormatsEMCAL/Cell.h"
#include "DataFormatsEMCAL/TriggerRecord.h"
#include "DataFormatsEMCAL/ErrorTypeFEE.h"
#include "Headers/DataHeader.h"
#include "EMCALBase/Geometry.h"
#include "EMCALBase/Mapper.h"
Expand Down Expand Up @@ -43,6 +44,11 @@ class RawToCellConverterSpec : public framework::Task
/// \param hasDecodingErrors Option to swich on/off creating raw decoding error objects for later monitoring
RawToCellConverterSpec(int subspecification, bool hasDecodingErrors) : framework::Task(), mSubspecification(subspecification), mCreateRawDataErrors(hasDecodingErrors){};

/// \brief Constructor
/// \param subspecification Output subspecification for parallel running on multiple nodes
/// \param hasDecodingErrors Option to swich on/off creating raw decoding error objects for later monitoring
RawToCellConverterSpec(int subspecification, bool hasDecodingErrors, bool requireSubspecLinks) : framework::Task(), mSubspecification(subspecification), mCreateRawDataErrors(hasDecodingErrors), mRequireSubspecLinks(requireSubspecLinks){};

/// \brief Destructor
~RawToCellConverterSpec() override;

Expand Down Expand Up @@ -105,6 +111,14 @@ class RawToCellConverterSpec : public framework::Task
};
bool isLostTimeframe(framework::ProcessingContext& ctx) const;

/// \brief Get FEEID links for a certain subspecification
/// \param subspec Subspecification (FLP ID)
/// \return FEEID links
///
/// Accepted links for subspec 0 (EMCAL FLP): 0 - 23
/// Accepted links for subspec 1 (DCAL FLP): 24 - 39
std::vector<int> getLinksForSubspec(int subspec) const;

/// \brief Send data to output channels
/// \param cells Container with output cells for timeframe
/// \param triggers Container with trigger records for timeframe
Expand All @@ -124,6 +138,7 @@ class RawToCellConverterSpec : public framework::Task
bool mPrintTrailer = false; ///< Print RCU trailer
bool mDisablePedestalEvaluation = false; ///< Disable pedestal evaluation independent of settings in the RCU trailer
bool mCreateRawDataErrors = false; ///< Create raw data error objects for monitoring
bool mRequireSubspecLinks = false; ///< Process only FEEIDs for a certain subspecification (FLP)
std::chrono::time_point<std::chrono::system_clock> mReferenceTime; ///< Reference time for muting messages
Geometry* mGeometry = nullptr; ///!<! Geometry pointer
std::unique_ptr<MappingHandler> mMapper = nullptr; ///!<! Mapper
Expand All @@ -138,6 +153,11 @@ class RawToCellConverterSpec : public framework::Task
/// Refer to RawToCellConverterSpec::run for input and output specs
framework::DataProcessorSpec getRawToCellConverterSpec(bool askDISTSTF, bool disableDecodingErrors, int subspecification);

/// \brief Creating DataProcessorSpec for the EMCAL Cell Converter Spec
///
/// Refer to RawToCellConverterSpec::run for input and output specs
framework::DataProcessorSpec getRawToCellConverterSpecFLP(bool askDISTSTF, bool disableDecodingErrors, int subspecification);

} // namespace reco_workflow

} // namespace emcal
Expand Down
184 changes: 184 additions & 0 deletions Detectors/EMCAL/workflow/src/EventBuilderSpec.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction

#include <algorithm>
#include <iterator>
#include <unordered_map>
#include <gsl/span>

#include "DataFormatsEMCAL/Cell.h"
#include "DataFormatsEMCAL/TriggerRecord.h"
#include "Framework/DataDescriptorMatcher.h"
#include "Framework/DataDescriptorQueryBuilder.h"
#include "Framework/InputSpec.h"
#include "Framework/Logger.h"
#include "EMCALWorkflow/EventBuilderSpec.h"

using namespace o2::emcal::reco_workflow;

void EventBuilderSpec::init(framework::InitContext& ctx)
{
}

void EventBuilderSpec::run(framework::ProcessingContext& ctx)
{
auto dataorigin = o2::header::gDataOriginEMC;
std::unordered_map<o2::header::DataHeader::SubSpecificationType, gsl::span<const TriggerRecord>> triggers;
std::unordered_map<o2::header::DataHeader::SubSpecificationType, gsl::span<const Cell>> cells;

std::vector<Cell> outputCells;
std::vector<TriggerRecord> outputTriggers;

auto posCells = ctx.inputs().getPos("inputcells"),
posTriggerRecords = ctx.inputs().getPos("inputtriggers");
auto numSlotsCells = ctx.inputs().getNofParts(posCells),
numSlotsTriggerRecords = ctx.inputs().getNofParts(posTriggerRecords);
for (decltype(numSlotsCells) islot = 0; islot < numSlotsCells; islot++) {
auto celldata = ctx.inputs().getByPos(posCells, islot);
auto subspecification = framework::DataRefUtils::getHeader<header::DataHeader*>(celldata)->subSpecification;
// Discard message if it is a deadbeaf message (empty timeframe)
if (subspecification == 0xDEADBEEF) {
continue;
}
if (cells.find(subspecification) != cells.end()) {
LOG(error) << "Found multiple messages for cells for subspec " << subspecification << "!";
}
cells[subspecification] = ctx.inputs().get<gsl::span<o2::emcal::Cell>>(celldata);
LOG(info) << "Received " << cells[subspecification].size() << " cells for subspecification " << subspecification;
}
for (decltype(numSlotsTriggerRecords) islot = 0; islot < numSlotsTriggerRecords; islot++) {
auto trgrecorddata = ctx.inputs().getByPos(posTriggerRecords, islot);
auto subspecification = framework::DataRefUtils::getHeader<header::DataHeader*>(trgrecorddata)->subSpecification;
// Discard message if it is a deadbeaf message (empty timeframe)
if (subspecification == 0xDEADBEEF) {
continue;
}
if (triggers.find(subspecification) != triggers.end()) {
LOG(error) << "Found multiple messages for trigger records for subspec " << subspecification << "!";
}
triggers[subspecification] = ctx.inputs().get<gsl::span<o2::emcal::TriggerRecord>>(trgrecorddata);
LOG(info) << "Received " << triggers[subspecification].size() << " triggers for subspecification " << subspecification;
}
LOG(info) << "Received data from " << cells.size() << " subspecifications for cells and " << triggers.size() << " subspecifications for triggers";

for (auto interaction : connectRangesFromSubtimeframes(triggers)) {
int start = outputCells.size(),
ncellsEvent = 0;
for (auto [subspec, triggerrecord] : interaction.mRangesSubtimeframe) {
LOG(debug) << interaction.mInteractionRecord.bc << " / " << interaction.mInteractionRecord.orbit << ": Receiving " << triggerrecord.getEntries() << " digits for subspec " << subspec;
auto cellcont = cells.find(subspec);
if (cellcont != cells.end()) {
for (auto& cell : gsl::span<const Cell>(cellcont->second.data() + triggerrecord.getFirstEntry(), triggerrecord.getEntries())) {
outputCells.emplace_back(cell);
}
} else {
LOG(alarm) << "No cell container found for subspec " << subspec;
}
}
if (ncellsEvent) {
std::sort(outputCells.begin() + start, outputCells.begin() + start + ncellsEvent, [](const Cell& lhs, const Cell& rhs) { return lhs.getTower() < rhs.getTower(); });
}
outputTriggers.emplace_back(interaction.mInteractionRecord, interaction.mTriggerType, start, ncellsEvent);
}

LOG(info) << "Sending " << outputTriggers.size() << " trigger records and " << outputCells.size() << " cells";

ctx.outputs().snapshot(framework::Output{dataorigin, "CELLS", 0, framework::Lifetime::Timeframe}, outputCells);
ctx.outputs().snapshot(framework::Output{dataorigin, "CELLSTRGR", 0, framework::Lifetime::Timeframe}, outputTriggers);
}

std::set<EventBuilderSpec::RangeCollection> EventBuilderSpec::connectRangesFromSubtimeframes(const std::unordered_map<o2::header::DataHeader::SubSpecificationType, gsl::span<const TriggerRecord>>& triggerrecords) const
{
std::set<RangeCollection> events;

// Search interaction records from all subevents
// build also a map with indices of the trigger record in gsl span for each subspecification
std::unordered_map<o2::InteractionRecord, std::unordered_map<o2::header::DataHeader::SubSpecificationType, int>> allInteractions;
for (auto& [subspecification, trgrec] : triggerrecords) {
for (decltype(trgrec.size()) trgPos = 0; trgPos < trgrec.size(); trgPos++) {
auto eventIR = trgrec[trgPos].getBCData();
auto interactionPtr = allInteractions.find(eventIR);
if (interactionPtr == allInteractions.end()) {
allInteractions[eventIR] = {{subspecification, trgPos}};
} else {
interactionPtr->second.insert({subspecification, trgPos});
}
}
}

// iterate over all subevents for all bunch crossings
for (const auto& [collisionID, subevents] : allInteractions) {
RangeCollection nextevent;
nextevent.mInteractionRecord = collisionID;
bool first = true;
for (auto& [subspecification, indexInSubevent] : subevents) {
auto& eventTR = triggerrecords.find(subspecification)->second[indexInSubevent];
if (first) {
nextevent.mTriggerType = eventTR.getTriggerBits();
first = false;
}
nextevent.mRangesSubtimeframe.push_back({subspecification, o2::dataformats::RangeReference(eventTR.getFirstEntry(), eventTR.getNumberOfObjects())});
}
events.insert(nextevent);
}
return events;
}

o2::framework::DataProcessorSpec o2::emcal::reco_workflow::getEventBuilderSpec()
{
auto dataorigin = o2::header::gDataOriginEMC;
o2::framework::Inputs inputs;

// Match all inputs from EMC/CELLS and EMC/CELLSTRGR except subspec 0
using namespace o2::framework::data_matcher;
DataDescriptorMatcher cellInputMatcher = {
DataDescriptorMatcher::Op::And,
OriginValueMatcher{"EMC"},
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::And,
DescriptionValueMatcher{"CELLS"},
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::And,
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::Not,
SubSpecificationTypeValueMatcher{0}),
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::Just,
StartTimeValueMatcher(ContextRef{0}))))};

DataDescriptorMatcher triggerRecordInputMatcher = {
DataDescriptorMatcher::Op::And,
OriginValueMatcher{"EMC"},
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::And,
DescriptionValueMatcher{"CELLSTRGR"},
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::And,
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::Not,
SubSpecificationTypeValueMatcher{0}),
std::make_unique<DataDescriptorMatcher>(
DataDescriptorMatcher::Op::Just,
StartTimeValueMatcher(ContextRef{0}))))};

inputs.push_back({"inputcells", std::move(cellInputMatcher), o2::framework::Lifetime::Timeframe});
inputs.push_back({"inputtriggers", std::move(triggerRecordInputMatcher), o2::framework::Lifetime::Timeframe});

o2::framework::Outputs outputs{
{dataorigin, "CELLS", 0, o2::framework::Lifetime::Timeframe},
{dataorigin, "CELLSTRGR", 0, o2::framework::Lifetime::Timeframe}};

return o2::framework::DataProcessorSpec{"EMCALEventBuilder",
inputs,
outputs,
o2::framework::adaptFromTask<EventBuilderSpec>(),
o2::framework::Options{}};
}
Loading