|
| 1 | +// Copyright CERN and copyright holders of ALICE O2. This software is |
| 2 | +// distributed under the terms of the GNU General Public License v3 (GPL |
| 3 | +// Version 3), copied verbatim in the file "COPYING". |
| 4 | +// |
| 5 | +// See http://alice-o2.web.cern.ch/license for full licensing information. |
| 6 | +// |
| 7 | +// In applying this license CERN does not waive the privileges and immunities |
| 8 | +// granted to it by virtue of its status as an Intergovernmental Organization |
| 9 | +// or submit itself to any jurisdiction. |
| 10 | + |
| 11 | +#include "EMCALDigitizerSpec.h" |
| 12 | +#include "Framework/ControlService.h" |
| 13 | +#include "Framework/DataProcessorSpec.h" |
| 14 | +#include "Framework/DataRefUtils.h" |
| 15 | +#include "Framework/Lifetime.h" |
| 16 | +#include "Headers/DataHeader.h" |
| 17 | +#include "TStopwatch.h" |
| 18 | +#include "Steer/HitProcessingManager.h" // for RunContext |
| 19 | +#include "TChain.h" |
| 20 | + |
| 21 | +#include "EMCALSimulation/Digitizer.h" |
| 22 | +#include "DataFormatsParameters/GRPObject.h" |
| 23 | +#include <SimulationDataFormat/MCCompLabel.h> |
| 24 | +#include <SimulationDataFormat/MCTruthContainer.h> |
| 25 | +#include "DetectorsBase/GeometryManager.h" |
| 26 | + |
| 27 | +using namespace o2::framework; |
| 28 | +using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; |
| 29 | + |
| 30 | +namespace o2 |
| 31 | +{ |
| 32 | +namespace emcal |
| 33 | +{ |
| 34 | + |
| 35 | +// helper function which will be offered as a service |
| 36 | +template <typename T> |
| 37 | +void retrieveHits(std::vector<TChain*> const& chains, |
| 38 | + const char* brname, |
| 39 | + int sourceID, |
| 40 | + int entryID, |
| 41 | + std::vector<T>* hits) |
| 42 | +{ |
| 43 | + auto br = chains[sourceID]->GetBranch(brname); |
| 44 | + if (!br) { |
| 45 | + LOG(ERROR) << "No branch found"; |
| 46 | + return; |
| 47 | + } |
| 48 | + br->SetAddress(&hits); |
| 49 | + br->GetEntry(entryID); |
| 50 | +} |
| 51 | + |
| 52 | +DataProcessorSpec getEMCALDigitizerSpec(int channel) |
| 53 | +{ |
| 54 | + // setup of some data structures shared between init and processing functions |
| 55 | + // (a shared pointer is used since then automatic cleanup is guaranteed with a lifetime beyond |
| 56 | + // one process call) |
| 57 | + auto simChains = std::make_shared<std::vector<TChain*>>(); |
| 58 | + |
| 59 | + // the instance of the actual digitizer |
| 60 | + auto digitizer = std::make_shared<o2::EMCAL::Digitizer>(); |
| 61 | + |
| 62 | + // containers for digits and labels |
| 63 | + auto digits = std::make_shared<std::vector<o2::EMCAL::Digit>>(); |
| 64 | + auto digitsAccum = std::make_shared<std::vector<o2::EMCAL::Digit>>(); // accumulator for all digits |
| 65 | + auto labels = std::make_shared<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>(); |
| 66 | + |
| 67 | + // the actual processing function which get called whenever new data is incoming |
| 68 | + auto process = [simChains, digitizer, digits, digitsAccum, labels, channel](ProcessingContext& pc) { |
| 69 | + static bool finished = false; |
| 70 | + if (finished) { |
| 71 | + return; |
| 72 | + } |
| 73 | + // RS: at the moment using hardcoded flag for continuos readout |
| 74 | + static o2::parameters::GRPObject::ROMode roMode = o2::parameters::GRPObject::CONTINUOUS; |
| 75 | + |
| 76 | + // read collision context from input |
| 77 | + auto context = pc.inputs().get<o2::steer::RunContext*>("collisioncontext"); |
| 78 | + auto& timesview = context->getEventRecords(); |
| 79 | + LOG(DEBUG) << "GOT " << timesview.size() << " COLLISSION TIMES"; |
| 80 | + |
| 81 | + // if there is nothing to do ... return |
| 82 | + if (timesview.size() == 0) { |
| 83 | + return; |
| 84 | + } |
| 85 | + |
| 86 | + TStopwatch timer; |
| 87 | + timer.Start(); |
| 88 | + |
| 89 | + LOG(INFO) << " CALLING EMCAL DIGITIZATION "; |
| 90 | + |
| 91 | + static std::vector<o2::EMCAL::Hit> hits; |
| 92 | + o2::dataformats::MCTruthContainer<o2::MCCompLabel> labelAccum; |
| 93 | + |
| 94 | + auto& eventParts = context->getEventParts(); |
| 95 | + // loop over all composite collisions given from context |
| 96 | + // (aka loop over all the interaction records) |
| 97 | + for (int collID = 0; collID < timesview.size(); ++collID) { |
| 98 | + digitizer->setEventTime(timesview[collID].timeNS); |
| 99 | + |
| 100 | + // for each collision, loop over the constituents event and source IDs |
| 101 | + // (background signal merging is basically taking place here) |
| 102 | + for (auto& part : eventParts[collID]) { |
| 103 | + digitizer->setCurrEvID(part.entryID); |
| 104 | + digitizer->setCurrSrcID(part.sourceID); |
| 105 | + |
| 106 | + // get the hits for this event and this source |
| 107 | + hits.clear(); |
| 108 | + retrieveHits(*simChains.get(), "EMCHit", part.sourceID, part.entryID, &hits); |
| 109 | + |
| 110 | + LOG(INFO) << "For collision " << collID << " eventID " << part.entryID << " found " << hits.size() << " hits "; |
| 111 | + |
| 112 | + // call actual digitization procedure |
| 113 | + labels->clear(); |
| 114 | + digits->clear(); |
| 115 | + digitizer->process(hits, *digits.get()); |
| 116 | + // copy digits into accumulator |
| 117 | + std::copy(digits->begin(), digits->end(), std::back_inserter(*digitsAccum.get())); |
| 118 | + labelAccum.mergeAtBack(*labels); |
| 119 | + LOG(INFO) << "Have " << digits->size() << " digits "; |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + LOG(INFO) << "Have " << labelAccum.getNElements() << " EMCAL labels "; |
| 124 | + // here we have all digits and we can send them to consumer (aka snapshot it onto output) |
| 125 | + pc.outputs().snapshot(Output{ "EMC", "DIGITS", 0, Lifetime::Timeframe }, *digitsAccum.get()); |
| 126 | + pc.outputs().snapshot(Output{ "EMC", "DIGITSMCTR", 0, Lifetime::Timeframe }, labelAccum); |
| 127 | + LOG(INFO) << "EMCAL: Sending ROMode= " << roMode << " to GRPUpdater"; |
| 128 | + pc.outputs().snapshot(Output{ "EMC", "ROMode", 0, Lifetime::Timeframe }, roMode); |
| 129 | + |
| 130 | + timer.Stop(); |
| 131 | + LOG(INFO) << "Digitization took " << timer.CpuTime() << "s"; |
| 132 | + |
| 133 | + // we should be only called once; tell DPL that this process is ready to exit |
| 134 | + pc.services().get<ControlService>().readyToQuit(false); |
| 135 | + finished = true; |
| 136 | + }; |
| 137 | + |
| 138 | + // init function returning the lambda taking a ProcessingContext |
| 139 | + auto initIt = [simChains, process, digitizer, labels](InitContext& ctx) { |
| 140 | + // setup the input chain for the hits |
| 141 | + simChains->emplace_back(new TChain("o2sim")); |
| 142 | + |
| 143 | + // add the main (background) file |
| 144 | + simChains->back()->AddFile(ctx.options().get<std::string>("simFile").c_str()); |
| 145 | + |
| 146 | + // maybe add a particular signal file |
| 147 | + auto signalfilename = ctx.options().get<std::string>("simFileS"); |
| 148 | + if (signalfilename.size() > 0) { |
| 149 | + simChains->emplace_back(new TChain("o2sim")); |
| 150 | + simChains->back()->AddFile(signalfilename.c_str()); |
| 151 | + } |
| 152 | + |
| 153 | + // make sure that the geometry is loaded (TODO will this be done centrally?) |
| 154 | + if (!gGeoManager) { |
| 155 | + o2::Base::GeometryManager::loadGeometry(); |
| 156 | + } |
| 157 | + auto geom = o2::EMCAL::Geometry::GetInstance("EMCAL_COMPLETE", "Geant4", "EMV-EMCAL"); |
| 158 | + // init digitizer |
| 159 | + digitizer->setGeometry(geom); |
| 160 | + digitizer->init(); |
| 161 | + |
| 162 | + // return the actual processing function which is now setup/configured |
| 163 | + return process; |
| 164 | + }; |
| 165 | + |
| 166 | + // create the full data processor spec using |
| 167 | + // a name identifier |
| 168 | + // input description |
| 169 | + // algorithmic description (here a lambda getting called once to setup the actual processing function) |
| 170 | + // options that can be used for this processor (here: input file names where to take the hits) |
| 171 | + return DataProcessorSpec{ |
| 172 | + "EMCALDigitizer", Inputs{ InputSpec{ "collisioncontext", "SIM", "COLLISIONCONTEXT", |
| 173 | + static_cast<SubSpecificationType>(channel), Lifetime::Timeframe } }, |
| 174 | + Outputs{ OutputSpec{ "EMC", "DIGITS", 0, Lifetime::Timeframe }, |
| 175 | + OutputSpec{ "EMC", "DIGITSMCTR", 0, Lifetime::Timeframe }, |
| 176 | + OutputSpec{ "EMC", "ROMode", 0, Lifetime::Timeframe } }, |
| 177 | + AlgorithmSpec{ initIt }, |
| 178 | + Options{ { "simFile", VariantType::String, "o2sim.root", { "Sim (background) input filename" } }, |
| 179 | + { "simFileS", VariantType::String, "", { "Sim (signal) input filename" } }, |
| 180 | + { "pileup", VariantType::Int, 1, { "whether to run in continuous time mode" } } } |
| 181 | + // I can't use VariantType::Bool as it seems to have a problem |
| 182 | + }; |
| 183 | +} |
| 184 | +} // end namespace emcal |
| 185 | +} // end namespace o2 |
0 commit comments