diff --git a/Detectors/PHOS/workflow/CMakeLists.txt b/Detectors/PHOS/workflow/CMakeLists.txt index d4d3d72544792..374940f3558d1 100644 --- a/Detectors/PHOS/workflow/CMakeLists.txt +++ b/Detectors/PHOS/workflow/CMakeLists.txt @@ -20,6 +20,8 @@ o2_add_library(PHOSWorkflow src/EntropyEncoderSpec.cxx src/EntropyDecoderSpec.cxx src/WriterSpec.cxx + src/EventBuilderSpec.cxx + src/StandaloneAODProducerSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsPHOS O2::DPLUtils O2::PHOSBase @@ -41,3 +43,13 @@ o2_add_executable(cell-writer-workflow COMPONENT_NAME phos SOURCES src/cell-writer-workflow.cxx PUBLIC_LINK_LIBRARIES O2::PHOSWorkflow) + +o2_add_executable(event-builder + COMPONENT_NAME phos + SOURCES src/event-builder-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::PHOSWorkflow) + +o2_add_executable(standalone-aod-producer-workflow + COMPONENT_NAME phos + SOURCES src/phos-standalone-aod-producer-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::PHOSWorkflow) diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/EventBuilderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/EventBuilderSpec.h new file mode 100644 index 0000000000000..309ac9fda703c --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/EventBuilderSpec.h @@ -0,0 +1,64 @@ +// 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 "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" + +namespace o2 +{ + +namespace phos +{ +class TriggerRecord; +class Cell; + +/// \class EventBuilderSpec +/// \brief Class merges subevents from two FLPs +/// \ingroup PHOSworkflow +/// \author Dmitri Peresunko, NRC "Kurchatov institute" +/// \since March, 2022 +/// +/// Merge subevents from two FLPs. FLPs send messages with non-zero subspecifications +/// take TrigRecs with same BC stamps and copy corresponding cells +/// sells are sorted, so find subspec with smaller absId and copy it first (exclude/handle trigger cells!) +/// check if all halves of events were found, otherwise send warning +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: + class SubspecSet + { + public: + SubspecSet(gsl::span r, gsl::span c) + { + trSpan = r; + cellSpan = c; + } + ~SubspecSet() = default; + gsl::span trSpan; + gsl::span cellSpan; + }; +}; + +o2::framework::DataProcessorSpec getEventBuilderSpec(); + +} // namespace phos + +} // namespace o2 diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h index 03fa7bdac9ea6..f5a9b6bc32e5a 100644 --- a/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/RawToCellConverterSpec.h @@ -40,7 +40,7 @@ class RawToCellConverterSpec : public framework::Task public: /// \brief Constructor /// \param propagateMC If true the MCTruthContainer is propagated to the output - RawToCellConverterSpec() : framework::Task(){}; + RawToCellConverterSpec(unsigned int flpId) : mflpId(flpId), framework::Task(){}; /// \brief Destructor ~RawToCellConverterSpec() override = default; @@ -65,6 +65,7 @@ class RawToCellConverterSpec : public framework::Task bool mCombineGHLG = true; ///< Combine or not HG and LG channels (def: combine, LED runs: not combine) bool mPedestalRun = false; ///< Analyze pedestal run (calculate pedestal mean and RMS) int mLastSize = 0; ///< size of last send list of cells to reserve same in next bunch + unsigned int mflpId = 0; ///< subspec of output stream std::unique_ptr mDecoder; ///! mRawFitter; ///!, 14> mTmpCells; ///< Temporary cells storage to all 14 DLL @@ -78,7 +79,7 @@ class RawToCellConverterSpec : public framework::Task /// \brief Creating DataProcessorSpec for the PHOS Cell Converter Spec /// /// Refer to RawToCellConverterSpec::run for input and output specs -framework::DataProcessorSpec getRawToCellConverterSpec(int flpId); +framework::DataProcessorSpec getRawToCellConverterSpec(unsigned int flpId); } // namespace reco_workflow diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/StandaloneAODProducerSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/StandaloneAODProducerSpec.h new file mode 100644 index 0000000000000..90db7454577b4 --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/StandaloneAODProducerSpec.h @@ -0,0 +1,52 @@ +// 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. + +/// @file StandaloneAODProducerSpec.h +/// @brief Convert CTF (EncodedBlocks) to AO2D PHOS standalone + +#ifndef O2_PHOS_STANDALONEAODPRODUCERSPEC_SPEC +#define O2_PHOS_STANDALONEAODPRODUCERSPEC_SPEC + +#include "Framework/AnalysisDataModel.h" +#include "Framework/AnalysisHelpers.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include + +namespace o2 +{ +namespace phos +{ + +class StandaloneAODProducerSpec : public o2::framework::Task +{ + public: + StandaloneAODProducerSpec(); + ~StandaloneAODProducerSpec() override = default; + void run(o2::framework::ProcessingContext& pc) final; + void init(o2::framework::InitContext& ic) final; + void endOfStream(o2::framework::EndOfStreamContext& ec) final; + + private: + int64_t mTFNumber = -1; // Timeframe ID + int mRunNumber = -1; // Run number + uint32_t mCaloAmp = 0xFFFFFF00; // 15 bits + uint32_t mCaloTime = 0xFFFFFF00; // 15 bits + TStopwatch mTimer; +}; + +/// create a processor spec +framework::DataProcessorSpec getPHOSStandaloneAODProducerSpec(); + +} // namespace phos +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/PHOS/workflow/src/EventBuilderSpec.cxx b/Detectors/PHOS/workflow/src/EventBuilderSpec.cxx new file mode 100644 index 0000000000000..e8344b69fffe5 --- /dev/null +++ b/Detectors/PHOS/workflow/src/EventBuilderSpec.cxx @@ -0,0 +1,202 @@ +// 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 "FairLogger.h" + +#include "DataFormatsPHOS/Cell.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "PHOSWorkflow/EventBuilderSpec.h" +#include "Framework/ControlService.h" +#include "CommonDataFormat/InteractionRecord.h" +#include + +using namespace o2::phos; + +void EventBuilderSpec::init(framework::InitContext& ctx) +{ +} + +void EventBuilderSpec::run(framework::ProcessingContext& ctx) +{ + // Merge subevents from two FLPs. FLPs send messages with non-zero subspecifications + // take TrigRecs with same BC stamps and copy corresponding cells + // sells are sorted, so find subspec with smaller absId and copy it first (exclude/handle trigger cells!) + // check if all halves of events were found, otherwise send warning + + std::vector outputCells; + std::vector outputTriggers; + int nOutCells = 0; + + int posCells = ctx.inputs().getPos("inputcells"); + int posTriggerRecords = ctx.inputs().getPos("inputtriggers"); + int numSlotsCells = ctx.inputs().getNofParts(posCells); + int numSlotsTriggerRecords = ctx.inputs().getNofParts(posTriggerRecords); + + std::vector subspecs; + + // Combine pairs (trigRec,cells) from different subspecs + for (int islot = 0; islot < numSlotsTriggerRecords; islot++) { + auto trgrecorddata = ctx.inputs().getByPos(posTriggerRecords, islot); + auto subspecification = framework::DataRefUtils::getHeader(trgrecorddata)->subSpecification; + // Discard message if it is a deadbeaf message (empty timeframe) + if (subspecification == 0xDEADBEEF) { + continue; + } + auto celldata = ctx.inputs().getByPos(posCells, islot); + // check if cells from the same source? + // if not try to find proper slot + if (framework::DataRefUtils::getHeader(celldata)->subSpecification != subspecification) { + for (int islotCell = 0; islotCell < numSlotsCells; islotCell++) { + celldata = ctx.inputs().getByPos(posCells, islotCell); + if (framework::DataRefUtils::getHeader(celldata)->subSpecification == subspecification) { + break; + } + } + // if still not found pair + if (framework::DataRefUtils::getHeader(celldata)->subSpecification != subspecification) { + LOG(error) << "Can not find cells for TrigRecords with subspecification " << subspecification; + continue; + } + } + subspecs.emplace_back(ctx.inputs().get>(trgrecorddata), + ctx.inputs().get>(celldata)); + } + + // Cells in vectors are sorted. Copy in correct order to preserve sorting + int first1 = 0; // which subspec should go first: 0: undefined yet, 1:[0],2:[1], + if (subspecs.size() == 1) { // one input, just copy to output + for (const Cell& c : subspecs[0].cellSpan) { + outputCells.emplace_back(c); + } + for (const TriggerRecord& r : subspecs[0].trSpan) { + outputTriggers.emplace_back(r); + } + + } else { + if (subspecs.size() > 2) { // error + LOG(error) << " Too many subspecs for event building:" << subspecs.size(); + } else { // combine two subspecs + auto tr1 = subspecs[0].trSpan.begin(); + auto tr2 = subspecs[1].trSpan.begin(); + while (tr1 != subspecs[0].trSpan.end() && tr2 != subspecs[1].trSpan.end()) { + if (tr1->getBCData() == tr2->getBCData()) { // OK, copy + if (first1 == 0) { // order of subspecs not yet defined + if (subspecs[0].cellSpan.size() > 0) { + short absId = subspecs[0].cellSpan[0].getAbsId(); + if (absId > 0) { // this is readout cell + first1 = 1 + (absId - 1) / (2 * 64 * 56); + } else { // TRU cell + first1 = 1 + (subspecs[0].cellSpan[0].getTRUId() - 1) / (8 * 28 * 7); + } + } else { + if (subspecs[1].cellSpan.size() > 0) { + short absId = subspecs[1].cellSpan[0].getAbsId(); + if (absId > 0) { // this is readout cell + first1 = 2 - (absId - 1) / (2 * 64 * 56); + } else { // TRU cell + first1 = 2 - (subspecs[1].cellSpan[0].getTRUId() - 1) / (8 * 28 * 7); + } + } + // if both lists are empty, keep first1 zero and try next time + } + } + + gsl::details::span_iterator itC1, end1, itC2, end2; + if (first1 > 1) { // copy first set1 then set2 + itC1 = subspecs[0].cellSpan.begin() + tr1->getFirstEntry(); + end1 = subspecs[0].cellSpan.begin() + tr1->getFirstEntry() + tr1->getNumberOfObjects(); + itC2 = subspecs[1].cellSpan.begin() + tr2->getFirstEntry(); + end2 = subspecs[1].cellSpan.begin() + tr2->getFirstEntry() + tr2->getNumberOfObjects(); + } else { + itC1 = subspecs[1].cellSpan.begin() + tr2->getFirstEntry(); + end1 = subspecs[1].cellSpan.begin() + tr2->getFirstEntry() + tr2->getNumberOfObjects(); + itC2 = subspecs[0].cellSpan.begin() + tr1->getFirstEntry(); + end2 = subspecs[0].cellSpan.begin() + tr1->getFirstEntry() + tr1->getNumberOfObjects(); + } + // first copy readout Cells from both events, then trigger + while (itC1 != end1) { + if (itC1->getAbsId() == 0) { // TRU cells further + break; + } + outputCells.emplace_back(*itC1); + ++itC1; + } + while (itC2 != end2) { + if (itC2->getAbsId() == 0) { + break; + } + outputCells.emplace_back(*itC2); + ++itC2; + } + // Copy trigger + while (itC1 != end1) { + outputCells.emplace_back(*itC1); + ++itC1; + } + while (itC2 != end2) { + outputCells.emplace_back(*itC2); + ++itC2; + } + + outputTriggers.emplace_back(tr1->getBCData(), nOutCells, outputCells.size() - nOutCells); + nOutCells = outputCells.size(); + ++tr1; + ++tr2; + } else { // inconsistent BCs + // find smaller BC and copy one part and mark as missing second part + if (tr1->getBCData() < tr2->getBCData()) { + auto itC1 = subspecs[0].cellSpan.begin() + tr1->getFirstEntry(); + auto end1 = subspecs[0].cellSpan.begin() + tr1->getFirstEntry() + tr1->getNumberOfObjects(); + while (itC1 != end1) { + outputCells.emplace_back(*itC1); + } + outputTriggers.emplace_back(tr1->getBCData(), nOutCells, outputCells.size() - nOutCells); + nOutCells = outputCells.size(); + ++tr1; + } else { + auto itC1 = subspecs[1].cellSpan.begin() + tr2->getFirstEntry(); + auto end1 = subspecs[1].cellSpan.begin() + tr2->getFirstEntry() + tr2->getNumberOfObjects(); + while (itC1 != end1) { + outputCells.emplace_back(*itC1); + } + outputTriggers.emplace_back(tr2->getBCData(), nOutCells, outputCells.size() - nOutCells); + nOutCells = outputCells.size(); + ++tr2; + } + } + } + if (tr1 != subspecs[0].trSpan.end() || tr2 != subspecs[1].trSpan.end()) { + LOG(error) << "Inconsistent number of TriggerRecords in subsec 1 and 2: " << subspecs[0].trSpan.size() << " and " << subspecs[1].trSpan.size(); + } + } + } + + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginPHS, "CELLS", 0, framework::Lifetime::Timeframe}, outputCells); + ctx.outputs().snapshot(framework::Output{o2::header::gDataOriginPHS, "CELLTRIGREC", 0, framework::Lifetime::Timeframe}, outputTriggers); +} + +o2::framework::DataProcessorSpec o2::phos::getEventBuilderSpec() +{ + std::vector inputs; + inputs.emplace_back("inputcells", "PHS", "CELLS", 1, o2::framework::Lifetime::Optional); // Input subspec 1, output subspec 0 + inputs.emplace_back("inputtriggers", "PHS", "CELLTRIGREC", 1, o2::framework::Lifetime::Optional); // Input subspec 1, output subspec 0 + // output should be anyway generated by raw converter. Or not? + // inputs.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe); + + o2::framework::Outputs outputs{ + {o2::header::gDataOriginPHS, "CELLS", 0, o2::framework::Lifetime::Timeframe}, // output is zero subspec + {o2::header::gDataOriginPHS, "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe}}; + + return o2::framework::DataProcessorSpec{"PHOSEventBuilder", + inputs, + outputs, + o2::framework::adaptFromTask(), + o2::framework::Options{}}; +} diff --git a/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx b/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx index 7034070f9f506..e622401ef2fc8 100644 --- a/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx +++ b/Detectors/PHOS/workflow/src/RawToCellConverterSpec.cxx @@ -100,9 +100,9 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); } mOutputCells.clear(); - ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", 0, o2::framework::Lifetime::Timeframe}, mOutputCells); + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", mflpId, o2::framework::Lifetime::Timeframe}, mOutputCells); mOutputTriggerRecords.clear(); - ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", mflpId, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); mOutputHWErrors.clear(); ctx.outputs().snapshot(o2::framework::Output{"PHS", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); if (mFillChi2) { @@ -257,15 +257,15 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) mLastSize = 1.1 * mOutputCells.size(); LOG(debug) << "[PHOSRawToCellConverter - run] Writing " << mOutputCells.size() << " cells ..."; - ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", 0, o2::framework::Lifetime::Timeframe}, mOutputCells); - ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", 0, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", mflpId, o2::framework::Lifetime::Timeframe}, mOutputCells); + ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", mflpId, o2::framework::Lifetime::Timeframe}, mOutputTriggerRecords); ctx.outputs().snapshot(o2::framework::Output{"PHS", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe}, mOutputHWErrors); if (mFillChi2) { ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLFITQA", 0, o2::framework::Lifetime::QA}, mOutputFitChi); } } -o2::framework::DataProcessorSpec o2::phos::reco_workflow::getRawToCellConverterSpec(int flpId) +o2::framework::DataProcessorSpec o2::phos::reco_workflow::getRawToCellConverterSpec(unsigned int flpId) { std::vector inputs; inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"PHS", "RAWDATA"}, o2::framework::Lifetime::Optional); @@ -275,13 +275,13 @@ o2::framework::DataProcessorSpec o2::phos::reco_workflow::getRawToCellConverterS std::vector outputs; outputs.emplace_back("PHS", "CELLS", flpId, o2::framework::Lifetime::Timeframe); outputs.emplace_back("PHS", "CELLTRIGREC", flpId, o2::framework::Lifetime::Timeframe); - outputs.emplace_back("PHS", "RAWHWERRORS", flpId, o2::framework::Lifetime::Timeframe); - outputs.emplace_back("PHS", "CELLFITQA", flpId, o2::framework::Lifetime::QA); + outputs.emplace_back("PHS", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("PHS", "CELLFITQA", 0, o2::framework::Lifetime::QA); return o2::framework::DataProcessorSpec{"PHOSRawToCellConverterSpec", inputs, // o2::framework::select("A:PHS/RAWDATA"), outputs, - o2::framework::adaptFromTask(), + o2::framework::adaptFromTask(flpId), o2::framework::Options{ {"presamples", o2::framework::VariantType::Int, 2, {"presamples time offset"}}, {"fitmethod", o2::framework::VariantType::String, "default", {"Fit method (default or semigaus)"}}, diff --git a/Detectors/PHOS/workflow/src/RecoWorkflow.cxx b/Detectors/PHOS/workflow/src/RecoWorkflow.cxx index c2f93d1ae1d2a..8f61fd9216ec9 100644 --- a/Detectors/PHOS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/PHOS/workflow/src/RecoWorkflow.cxx @@ -85,13 +85,13 @@ o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, // no explicit raw reader ?? if (isEnabled(OutputType::Cells)) { - specs.emplace_back(o2::phos::reco_workflow::getRawToCellConverterSpec(flpId)); + specs.emplace_back(o2::phos::reco_workflow::getRawToCellConverterSpec(static_cast(flpId))); if (!disableRootOut) { specs.emplace_back(o2::phos::getCellWriterSpec(false)); } } if (isEnabled(OutputType::Clusters)) { - specs.emplace_back(o2::phos::reco_workflow::getRawToCellConverterSpec(flpId)); + specs.emplace_back(o2::phos::reco_workflow::getRawToCellConverterSpec(static_cast(flpId))); specs.emplace_back(o2::phos::reco_workflow::getCellClusterizerSpec(false, fullCluOut)); // no MC propagation if (!disableRootOut) { specs.emplace_back(o2::phos::getClusterWriterSpec(false)); diff --git a/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx b/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx new file mode 100644 index 0000000000000..d84f40458de43 --- /dev/null +++ b/Detectors/PHOS/workflow/src/StandaloneAODProducerSpec.cxx @@ -0,0 +1,173 @@ +// 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 "PHOSWorkflow/StandaloneAODProducerSpec.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "DataFormatsPHOS/Cell.h" +#include "Framework/TableBuilder.h" +#include "Framework/AnalysisDataModel.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataTypes.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" +#include "Framework/TableBuilder.h" +#include "Framework/TableTreeHelpers.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace phos +{ + +StandaloneAODProducerSpec::StandaloneAODProducerSpec() +{ + mTimer.Stop(); + mTimer.Reset(); +} + +void StandaloneAODProducerSpec::init(o2::framework::InitContext& ic) +{ + mCaloAmp = 0xFFFFFFFF; + mCaloTime = 0xFFFFFFFF; + + mTFNumber = ic.options().get("aod-timeframe-id"); + mRunNumber = ic.options().get("run-number"); +} + +void StandaloneAODProducerSpec::run(ProcessingContext& pc) +{ + // auto cput = mTimer.CpuTime(); + // mTimer.Start(false); + + const auto* dh = o2::header::get(pc.inputs().getFirstValid(true).header); + uint64_t tfNumber; + if (mTFNumber == -1L) { + // TODO has to be made globally unique (by using absolute time of TF). For now is unique within the run + tfNumber = uint64_t(dh->firstTForbit) + (uint64_t(dh->runNumber) << 32); // getTFNumber(mStartIR, runNumber); + } else { + tfNumber = mTFNumber; + } + const int runNumber = (mRunNumber == -1) ? int(dh->runNumber) : mRunNumber; + + auto cells = pc.inputs().get>("cells"); + auto ctr = pc.inputs().get>("cellTriggerRecords"); + + LOG(debug) << "FOUND " << cells.size() << " PHOS cells in CTF"; + LOG(debug) << "FOUND " << ctr.size() << " PHOS tiggers in CTF"; + + auto& bcBuilder = pc.outputs().make(Output{"AOD", "BC"}); + auto& collisionsBuilder = pc.outputs().make(Output{"AOD", "COLLISION"}); + auto& caloCellsBuilder = pc.outputs().make(Output{"AOD", "CALO"}); + auto& caloCellsTRGTableBuilder = pc.outputs().make(Output{"AOD", "CALOTRIGGER"}); + + auto bcCursor = bcBuilder.cursor(); + auto collisionsCursor = collisionsBuilder.cursor(); + auto caloCellsCursor = caloCellsBuilder.cursor(); + auto caloCellsTRGTableCursor = caloCellsTRGTableBuilder.cursor(); + + uint64_t triggerMask = 1; + // loop over events + int indexBC = -1; + for (const auto& tr : ctr) { + indexBC++; + int firstCellInEvent = tr.getFirstEntry(); + int lastCellInEvent = firstCellInEvent + tr.getNumberOfObjects(); + for (int i = firstCellInEvent; i < lastCellInEvent; i++) { + const Cell c = cells[i]; + if (c.getTRU()) { + // TODO!!! test time? + caloCellsTRGTableCursor(0, + indexBC, + c.getTRUId(), // fastOrAbsId + c.getEnergy(), // lnAmplitude (dummy value) + (c.getType() == TRU2x2) ? 0 : 1, // triggerBits 0:L0 2x2, 1: L1 4x4 + 0); // caloType 0: PHOS + continue; + } + + // short absId = c.getAbsId(); + // if (isBadChannel(absId)) { + // continue; + // } + // fill table + caloCellsCursor(0, + indexBC, + c.getAbsId(), + o2::math_utils::detail::truncateFloatFraction(c.getEnergy(), mCaloAmp), + o2::math_utils::detail::truncateFloatFraction(c.getTime(), mCaloTime), + c.getType(), // HG/LG + 0); // hard coded for phos (-1 would be undefined, 0 phos) + } // end of cell loop + + auto bcID = tr.getBCData().toLong(); + bcCursor(0, + runNumber, + bcID, + triggerMask); + + // fill collision cursor + collisionsCursor(0, + indexBC, + 0., // X-Pos dummy value + 0., // Y Pos + 0., // Z Pos + 0, // cov 0 + 0, // cov 1 + 0, // cov 2 + 0, // cov 3 + 0, // cov 4 + 0, // cov 5 + 0, // vertex bit field for flags + 0, // chi2 + 0, // ncontributors + 0, // rel interaction time + 0); // vertex time stamp + + } // end of event loop + // std::cout << "Finished cell loop" << std::endl; + + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0, Lifetime::Timeframe}, tfNumber); + + mTimer.Stop(); +} + +void StandaloneAODProducerSpec::endOfStream(EndOfStreamContext& ec) +{ + LOGF(info, "PHOS Standalone AOD Producer total timing: Cpu: %.3e Real: %.3e s in %d slots", + mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); +} + +DataProcessorSpec getPHOSStandaloneAODProducerSpec() +{ + std::vector inputs; + inputs.emplace_back("cells", o2::header::gDataOriginPHS, "CELLS", 0, Lifetime::Timeframe); + inputs.emplace_back("cellTriggerRecords", o2::header::gDataOriginPHS, "CELLTRIGREC", 0, Lifetime::Timeframe); + + std::vector outputs; + outputs.emplace_back(OutputLabel{"O2bc"}, "AOD", "BC", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2collision"}, "AOD", "COLLISION", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2caloCell"}, "AOD", "CALO", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputLabel{"O2caloCellTRGR"}, "AOD", "CALOTRIGGER", 0, Lifetime::Timeframe); + outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); + + return DataProcessorSpec{ + "phos-standalone-aod-producer-workflow", + inputs, + outputs, + AlgorithmSpec{adaptFromTask()}, + Options{ + ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}}, + ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}}}}; +} + +} // namespace phos +} // namespace o2 diff --git a/Detectors/PHOS/workflow/src/event-builder-workflow.cxx b/Detectors/PHOS/workflow/src/event-builder-workflow.cxx new file mode 100644 index 0000000000000..588abb1fd54c4 --- /dev/null +++ b/Detectors/PHOS/workflow/src/event-builder-workflow.cxx @@ -0,0 +1,34 @@ +// 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 +#include +#include +#include "PHOSWorkflow/EventBuilderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/Logger.h" + +using namespace o2::framework; + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + wf.emplace_back(o2::phos::getEventBuilderSpec()); + return wf; +} \ No newline at end of file diff --git a/Detectors/PHOS/workflow/src/phos-standalone-aod-producer-workflow.cxx b/Detectors/PHOS/workflow/src/phos-standalone-aod-producer-workflow.cxx new file mode 100644 index 0000000000000..4182faf885d62 --- /dev/null +++ b/Detectors/PHOS/workflow/src/phos-standalone-aod-producer-workflow.cxx @@ -0,0 +1,43 @@ +// 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. + +/// @file standalone-aod-producer-workflow.cxx +/// @brief Conversion from CTF data -> AO2D standalone for PHOS (does not require other detectors like AODProducerWorklfow) + +#include "PHOSWorkflow/StandaloneAODProducerSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +// ------------------------------------------------------------------ + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); + wf.emplace_back(o2::phos::getPHOSStandaloneAODProducerSpec()); + return wf; +}