InfillChannels_module.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 // Class: InfillChannels
3 // Plugin Type: analyzer (art v3_05_01)
4 // File: InfillChannels_module.cc
5 //
6 // Alex Wilkinson - 08/03/21
7 ////////////////////////////////////////////////////////////////////////
8 
16 #include "fhiclcpp/ParameterSet.h"
18 
22 #include "lardataobj/RawData/raw.h"
25 
26 #include <TGeoVolume.h>
27 
28 #include <vector>
29 #include <string>
30 #include <map>
31 #include <algorithm>
32 #include <iterator>
33 #include <array>
34 
35 #include <torch/script.h>
36 #include <torch/torch.h>
37 
38 namespace Infill
39 {
40  class InfillChannels;
41 }
42 
44 {
45 public:
46  explicit InfillChannels(fhicl::ParameterSet const& p);
47 
48  // Plugins should not be copied or assigned.
49  InfillChannels(InfillChannels const&) = delete;
50  InfillChannels(InfillChannels&&) = delete;
51  InfillChannels& operator=(InfillChannels const&) = delete;
53 
54  // Required functions.
55  void produce(art::Event& e) override;
56 
57  // Selected optional functions.
58  void beginJob() override;
59  void endJob() override;
60 
61 private:
62  // Declare member data here.
64 
65  std::set<raw::ChannelID_t> fBadChannels;
66  std::set<raw::ChannelID_t> fNoisyChannels;
67  std::set<raw::ChannelID_t> fDeadChannels;
68 
69  std::set<readout::ROPID> fActiveRops;
70 
77 };
78 
80  : EDProducer{p},
81  fNetworkPath (p.get<std::string> ("NetworkPath")),
82  fNetworkNameInduction (p.get<std::string> ("NetworkNameInduction")),
83  fNetworkNameCollection (p.get<std::string> ("NetworkNameCollection")),
84  fInputLabel (p.get<std::string> ("InputLabel"))
85 {
86  consumes<std::vector<raw::RawDigit>>(fInputLabel);
87 
88  produces<std::vector<raw::RawDigit>>();
89 }
90 
92 {
93  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService>()->DataFor(e);
94  // Networks expect a fixed image size
95  if (detProp.NumberTimeSamples() > 6000) {
96  std::cerr << "InfillChannels_module.cc: Networks cannot handle more than 6000 time ticks\n";
97  std::abort();
98  }
99 
100  typedef std::array<short, 6000> vecAdc;
101  std::map<raw::ChannelID_t, vecAdc> infilledAdcs;
102  torch::Tensor maskedRopTensor;
103  torch::Tensor infilledRopTensor;
104 
105  auto digs = e.getHandle<std::vector<raw::RawDigit> >(fInputLabel);
106 
107  // Get infilled adc ROP by ROP
108  for (const readout::ROPID& currentRop : fActiveRops) {
109  maskedRopTensor = torch::zeros(
110  {1, 1 ,6000, fGeom->Nchannels(currentRop)}, torch::dtype(torch::kFloat32).device(torch::kCPU).requires_grad(false)
111  );
112  auto maskedRopTensorAccess = maskedRopTensor.accessor<float, 4>();
113 
114  const raw::ChannelID_t firstCh = fGeom->FirstChannelInROP(currentRop);
115 
116  // Fill ROP image
117  for (const raw::RawDigit& dig : *digs) {
118  if (fDeadChannels.count(dig.Channel())) continue;
119 
120  readout::ROPID rop = fGeom->ChannelToROP(dig.Channel());
121  if (rop != currentRop) continue;
122 
123  raw::RawDigit::ADCvector_t adcs(dig.Samples());
124  raw::Uncompress(dig.ADCs(), adcs, dig.Compression());
125 
126  for (unsigned int tick = 0; tick < adcs.size(); ++tick) {
127  const int adc = adcs[tick] ? int(adcs[tick]) - dig.GetPedestal() : 0;
128 
129  maskedRopTensorAccess[0][0][tick][dig.Channel() - firstCh] = adc;
130  }
131  }
132 
133  // Do the Infill
134  std::vector<torch::jit::IValue> inputs;
135  inputs.push_back(maskedRopTensor);
136  if (fGeom->SignalType(currentRop) == geo::kInduction) {
137  torch::NoGradGuard no_grad_guard;
138  infilledRopTensor = fInductionModule.forward(inputs).toTensor().detach();
139  }
140  else if (fGeom->SignalType(currentRop) == geo::kCollection) {
141  torch::NoGradGuard no_grad_guard;
142  infilledRopTensor = fCollectionModule.forward(inputs).toTensor().detach();
143  }
144 
145  // Store infilled ADC of dead channels
146  auto infilledRopTensorAccess = infilledRopTensor.accessor<float, 4>();
147  for (const raw::ChannelID_t ch : fDeadChannels) {
148  if (fGeom->ChannelToROP(ch) == currentRop) {
149  for (unsigned int tick = 0; tick < detProp.NumberTimeSamples(); ++tick) {
150  infilledAdcs[ch][tick] = (short)std::round(infilledRopTensorAccess[0][0][tick][ch - firstCh]);
151  }
152  }
153  } // Could break early if ch > last ch in currentRop to save a bit of looping?
154  }
155 
156  // Encode infilled ADC into RawDigit and put back onto event
157  auto infilledDigs = std::make_unique<std::vector<raw::RawDigit>>();
158  *infilledDigs = *digs;
159  for (raw::RawDigit& dig : *infilledDigs) {
160  if (infilledAdcs.count(dig.Channel())) {
161  raw::RawDigit::ADCvector_t infilledAdc(
162  infilledAdcs[dig.Channel()].begin(), (infilledAdcs[dig.Channel()].begin() + detProp.NumberTimeSamples())
163  );
164 
165  // Get new pedestal
166  auto infilledAdcMin = std::min_element(infilledAdc.begin(), infilledAdc.end());
167  short ped = *infilledAdcMin < 0 ? std::abs(*infilledAdcMin) + 1 : 0;
168  for (short& adc : infilledAdc) adc += ped;
169 
170  raw::Compress(infilledAdc, dig.Compression()); // need to consider compression parameters
171  dig = raw::RawDigit(dig.Channel(), dig.Samples(), infilledAdc, dig.Compression());
172  dig.SetPedestal(ped);
173  }
174  }
175  e.put(std::move(infilledDigs));
176 }
177 
179 {
181 
182  // Dead channels = bad channels + noisy channels
185 
186  std::merge(
187  fBadChannels.begin(), fBadChannels.end(), fNoisyChannels.begin(), fNoisyChannels.end(),
188  std::inserter(fDeadChannels, fDeadChannels.begin())
189  );
190 
191  // Get active ROPs (not facing a wall and has dead channels)
194  for (iRop = rBegin; iRop != rEnd; ++iRop) { // Iterate over ROPs in the detector
195  bool hasDeadCh = false;
196  for (raw::ChannelID_t ch : fDeadChannels) {
197  if (fGeom->ChannelToROP(ch) == *iRop) {
198  hasDeadCh = true;
199  break;
200  }
201  }
202  if (!hasDeadCh) continue; // Don't need to infill ROPs without dead channels
203 
204  for (const geo::TPCID tpcId : fGeom->ROPtoTPCs(*iRop)) {
205  const geo::TPCGeo tpc = fGeom->TPC(tpcId);
206  const TGeoVolume* tpcVol = tpc.ActiveVolume();
207 
208  if (tpcVol->Capacity() > 1000000) { // At least one of the ROP's TPCIDs needs to be active
209  // Networks expect a fixed image size
210  if(fGeom->SignalType(*iRop) == geo::kInduction && fGeom->Nchannels(*iRop) > 800) {
211  std::cerr << "InfillChannels_module.cc: Induction view network cannot handle more then 800 channels\n";
212  std::abort();
213  }
214  if(fGeom->SignalType(*iRop) == geo::kCollection && fGeom->Nchannels(*iRop) > 480) {
215  std::cerr << "InfillChannels_module.cc: Collection view network cannot handle more then 400 channels\n";
216  std::abort();
217  }
218 
219  fActiveRops.insert(*iRop);
220  break;
221  }
222  }
223  }
224 
225  // Check dead channels resemble the dead channels used for training
226  raw::ChannelID_t chGap = 1;
227  for (const raw::ChannelID_t ch : fDeadChannels) {
228  if (fDeadChannels.count(ch + 1)) {
229  ++chGap;
230  continue;
231  }
232  if (fGeom->ChannelToROP(ch - chGap) == fGeom->ChannelToROP(ch + 1)) {
233  if (fGeom->SignalType(ch) == geo::kCollection && chGap > 3) {
234  std::cerr << "There are dead channel gap larger than what was seen in training --- ";
235  std::cerr << "**Consider retraining collection plane infill network**" << std::endl;
236  }
237  else if (fGeom->SignalType(ch) == geo::kInduction && chGap > 2) {
238  std::cerr << "There are dead channel gap larger than what was seen in training --- ";
239  std::cerr << "**Consider retraining induction plane infill network**" << std::endl;
240  }
241  }
242  chGap = 1;
243  }
244 
245  // Load torchscripts
246  std::cout << "Loading modules..." << std::endl;
247  const char* networkPath = std::getenv(fNetworkPath.c_str());
248  if (networkPath == nullptr) {
249  std::cerr << "InfillChannels_module.cc: Environment variable " << fNetworkPath << " was not found";
250  std::abort();
251  }
252  const std::string networkLocInduction = std::string(networkPath) + "/" + fNetworkNameInduction;
253  const std::string networkLocCollection = std::string(networkPath) + "/" + fNetworkNameCollection;
254 
255  try {
256  fInductionModule = torch::jit::load(networkLocInduction);
257  std::cout << "Induction module loaded from " << networkLocInduction <<std::endl;
258  }
259  catch (const c10::Error& err) {
260  std::cerr << "error loading the model\n";
261  std::cerr << err.what();
262  }
263  try {
264  fCollectionModule = torch::jit::load(networkLocCollection);
265  std::cout << "Collection module loaded from " << networkLocCollection << std::endl;
266  }
267  catch (const c10::Error& err) {
268  std::cerr << "error loading the model\n";
269  std::cerr << err.what();
270  }
271 }
272 
274 {
275  // Implementation of optional member function here.
276 }
277 
void produce(art::Event &e) override
std::vector< geo::TPCID > ROPtoTPCs(readout::ROPID const &ropid) const
Returns a list of ID of TPCs the specified ROP spans.
std::set< raw::ChannelID_t > fDeadChannels
Collection of charge vs time digitized from a single readout channel.
Definition: RawDigit.h:69
const std::string fNetworkNameCollection
std::set< raw::ChannelID_t > fBadChannels
static constexpr BeginPos_t begin_pos
Definition: GeometryCore.h:107
Handle< PROD > getHandle(SelectorBase const &) const
Definition: DataViewImpl.h:382
std::string string
Definition: nybbler.cc:12
Base forward iterator browsing all readout plane IDs in the detector.
InfillChannels(fhicl::ParameterSet const &p)
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.h:20
Geometry information for a single TPC.
Definition: TPCGeo.h:38
static constexpr EndPos_t end_pos
Definition: GeometryCore.h:108
std::vector< short > ADCvector_t
Type representing a (compressed) vector of ADC counts.
Definition: RawDigit.h:73
virtual const provider_type * provider() const override
int16_t adc
Definition: CRTFragment.hh:202
SigType_t SignalType(geo::PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
art framework interface to geometry description
unsigned int Nchannels() const
Returns the number of TPC readout channels in the detector.
InfillChannels & operator=(InfillChannels const &)=delete
std::set< raw::ChannelID_t > fNoisyChannels
T abs(T value)
const double e
const geo::GeometryCore * fGeom
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
Signal from induction planes.
Definition: geo_types.h:145
#define Module
raw::ChannelID_t FirstChannelInROP(readout::ROPID const &ropid) const
Returns the ID of the first channel in the specified readout plane.
nvidia::inferenceserver::client::Error Error
Definition: triton_utils.h:15
std::string getenv(std::string const &name)
Definition: getenv.cc:15
def move(depos, offset)
Definition: depos.py:107
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Definition: electronics.h:75
p
Definition: test.py:223
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
const TGeoVolume * ActiveVolume() const
Definition: TPCGeo.h:119
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
Description of geometry of one entire detector.
void err(const char *fmt,...)
Definition: message.cpp:226
Class identifying a set of planes sharing readout channels.
readout::ROPID ChannelToROP(raw::ChannelID_t channel) const
const std::string fNetworkPath
const std::string fNetworkNameInduction
void SetPedestal(float ped, float sigma=1.)
Set pedestal and its RMS (the latter is 0 by default)
Definition: RawDigit.cxx:68
void Compress(std::vector< short > &adc, raw::Compress_t compress)
Compresses a raw data buffer.
Definition: raw.cxx:19
Access the description of detector geometry.
torch::jit::script::Module fInductionModule
TPCGeo const & TPC(unsigned int const tpc=0, unsigned int const cstat=0) const
Returns the specified TPC.
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
Interface for experiment-specific service for channel quality info.
void Uncompress(const std::vector< short > &adc, std::vector< short > &uncompressed, raw::Compress_t compress)
Uncompresses a raw data buffer.
Definition: raw.cxx:776
torch::jit::script::Module fCollectionModule
QTextStream & endl(QTextStream &s)
Signal from collection planes.
Definition: geo_types.h:146
def load(filename, jpath="depos")
Definition: depos.py:34
std::set< readout::ROPID > fActiveRops