All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FakeParticle_tool.cc
Go to the documentation of this file.
1 /**
2  * @file FakeParticle_tool.cc
3  *
4  * @brief This tool creates a fake particle and overlays on input data
5  *
6  */
7 
8 // Framework Includes
9 #include "art/Framework/Core/EDProducer.h"
10 #include "art/Framework/Principal/Event.h"
11 #include "art/Framework/Principal/Handle.h"
12 #include "art/Framework/Services/Registry/ServiceHandle.h"
13 #include "art/Persistency/Common/PtrMaker.h"
14 #include "art/Utilities/ToolMacros.h"
15 #include "cetlib/cpu_timer.h"
16 #include "fhiclcpp/ParameterSet.h"
17 #include "messagefacility/MessageLogger/MessageLogger.h"
18 
19 // LArSoft includes
22 
23 // ICARUS package includes
26 
27 #include "icarus_signal_processing/Filters/ICARUSFFT.h"
28 
29 // std includes
30 #include <string>
31 #include <iostream>
32 #include <memory>
33 
34 //------------------------------------------------------------------------------------------------------------------------------------------
35 // implementation follows
36 
37 namespace daq {
38 /**
39  * @brief FakeParticle class definiton
40  */
41 class FakeParticle : virtual public IFakeParticle
42 {
43 public:
44  /**
45  * @brief Constructor
46  *
47  * @param pset
48  */
49  explicit FakeParticle(fhicl::ParameterSet const &pset);
50 
51  /**
52  * @brief Destructor
53  */
54  ~FakeParticle();
55 
56  /**
57  * @brief Interface for configuring the particular algorithm tool
58  *
59  * @param ParameterSet The input set of parameters for configuration
60  */
61  virtual void configure(const fhicl::ParameterSet&) override;
62 
63  /**
64  * @brief Creates a fake particle and overlays on the input fragment
65  *
66  * @param waveforms The waveform container to place fake particle on
67  */
68  virtual void overlayFakeParticle(detinfo::DetectorClocksData const& clockData,
69  ArrayFloat& waveforms) override;
70 
71 private:
72 
73  // fhicl variables
74  std::vector<size_t> fWireEndPoints; //< Desired wire endpoints for our particle
75  size_t fStartTick; //< The tick for the start point of our particle
76  float fStartAngle; //< Angle (in degrees) for the trajectory
77  int fNumElectronsPerMM; //< The number of electrons/mm to deposit
78  size_t fPlaneToSimulate; //< The plane to simulate
79 
80  // Some useful variables
81  float fMMPerTick; //< Convert ticks in us to mm
82  float fMMPerWire; //< Convert wire pitch to mm
83  float fTanThetaTW; //< tangent of angle in time/wire space
84  float fTanTheta; //< tangent in euclidean space
85  float fSinTheta; //< sine of this angle
86  float fCosTheta; //< save some time by also doing cosine
87  std::vector<size_t> fTickEndPoints; //< Tick endpoints to overlay
88 
89  icarusutil::TimeVec fFFTTimeVec; //< Local time vector
90 
91  using FFTPointer = std::unique_ptr<icarus_signal_processing::ICARUSFFT<double>>;
92 
93  FFTPointer fFFT; //< Object to handle thread safe FFT
94 
95  const geo::Geometry* fGeometry; //< pointer to the Geometry service
96  const detinfo::DetectorProperties* fDetector; //< Pointer to the detector properties
97  icarusutil::SignalShapingICARUSService* fSignalShapingService; //< Access to the response functions
98 };
99 
100 FakeParticle::FakeParticle(fhicl::ParameterSet const &pset)
101 {
102  this->configure(pset);
103 
104  return;
105 }
106 
107 //------------------------------------------------------------------------------------------------------------------------------------------
108 
110 {
111 }
112 
113 //------------------------------------------------------------------------------------------------------------------------------------------
114 void FakeParticle::configure(fhicl::ParameterSet const &pset)
115 {
116  fWireEndPoints = pset.get<std::vector<size_t>>("WireEndPoints", std::vector<size_t>()={25,550});
117  fStartTick = pset.get<size_t >("StartTick", 1000);
118  fStartAngle = pset.get<float >("StartAngle", 45);
119  fNumElectronsPerMM = pset.get<int >("NumElectronsPerMM", 6000);
120  fPlaneToSimulate = pset.get<size_t >("PlaneToSimulate", 2);
121 
122  fGeometry = art::ServiceHandle<geo::Geometry const>{}.get();
123  fSignalShapingService = art::ServiceHandle<icarusutil::SignalShapingICARUSService>{}.get();
124 
125  // Convert ticks in us to mm by taking the drift velocity and multiplying by the tick period
126  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataForJob();
127  auto const detProp = art::ServiceHandle<detinfo::DetectorPropertiesService const>()->DataForJob(clockData);
128  double driftVelocity = detProp.DriftVelocity() * 10.; // should be mm/us
129  double samplingRate = sampling_rate(clockData) / 1000.; // sampling rate returned in ns
130 
131  fMMPerTick = driftVelocity * samplingRate;
132 
133  fMMPerWire = fGeometry->WirePitch() * 10.; // wire pitch returned in cm, want mm
134 
135  // Get slope (tan(theta)) and related angles
136  fTanTheta = std::tan(fStartAngle * M_PI / 180.);
137  fCosTheta = 1. / sqrt(1. + fTanTheta * fTanTheta);
138  fSinTheta = fTanTheta * fCosTheta;
139 
140  fTanThetaTW = fTanTheta * fMMPerWire / fMMPerTick;
141 
142  // Constrain x range
143  fWireEndPoints[1] = std::min(fWireEndPoints[1],size_t(576));
144 
145  // Now compute the tick start/end points
146  fTickEndPoints.push_back(fStartTick);
147  fTickEndPoints.push_back(size_t(std::round(fTanThetaTW * float(fWireEndPoints[1] - fWireEndPoints[0]) + fStartTick)));
148 
149  // Check to see if we have run off the end of our frame (max 576,4096)
150  if (fTickEndPoints[1] > size_t(4096))
151  {
152  // Probably should worry about the 90 degree case...
153  if (std::fabs(fStartAngle) < 90)
154  {
155  fWireEndPoints[1] = std::round(float(4096 - fStartTick) / fTanTheta + fWireEndPoints[0]);
156  fTickEndPoints[1] = 4096;
157  }
158  else
160  }
161 
162  // Now set up our plans for doing the convolution
163  int numberTimeSamples = fDetector->NumberTimeSamples();
164 
165  fFFTTimeVec.resize(numberTimeSamples,0.);
166 
167  fFFT = std::make_unique<icarus_signal_processing::ICARUSFFT<double>>(numberTimeSamples);
168 
169  return;
170 }
171 
173 {
174  // We have assumed the input waveform array will have 576 wires...
175  // Our "range" must be contained within that. By assumption we start at wire 0, so really just need
176  // to set the max range
177  size_t maxWire = std::min(waveforms.size(),fWireEndPoints[1]);
178 
179  // Create a temporary waveform to handle the input charge
180 // icarusutil::TimeVec tempWaveform(waveforms[0].size(),0.);
181 
182  // Also recover the gain
183  float asicGain = fSignalShapingService->GetASICGain(0) * sampling_rate(clockData) / 1000.; // something like 67.4
184 
185  // Get a base channel number for the plane we want
187 
188  // Recover the response function information for this channel
189  const icarus_tool::IResponse& response = fSignalShapingService->GetResponse(channel);
190 
191  // Also want the time offset for this channel
192  int timeOffset = fSignalShapingService->ResponseTOffset(channel);
193 
194  // Loop over the wire range
195  for(size_t wireIdx = fWireEndPoints[0]; wireIdx < maxWire; wireIdx++)
196  {
197  // As the track angle becomes more parallel to the electron drift direction there will be more charge
198  // deposited per mm that will impact a given wire. So we need to try to accommodate this here.
199  // Start by computing the starting tick (based on how far we have gone so far)
200  size_t startTick = size_t(fTanThetaTW * float(wireIdx - fWireEndPoints[0])) + fTickEndPoints[0];
201 
202  // If this has gone outside the maximum tick then we are done
203  if (!(startTick < fTickEndPoints[1] && startTick < fFFTTimeVec.size())) break;
204 
205  // Begin by computing the number of ticks for this given wire, converted to tick units and
206  // always at least one tick
207  size_t deltaTicks = size_t(fMMPerWire * std::fabs(fTanTheta)) + 1;
208  size_t endTick = startTick + deltaTicks;
209 
210  // Trim back if we look to step outside of the max range
211  endTick = std::min(endTick,fTickEndPoints[1]);
212  endTick = std::min(endTick,fFFTTimeVec.size());
213 
214  // Ok, now loop through the ticks and deposit charge.
215  // This will be the number of electrons per mm (input)
216  // x the total arc length of track for this step
217  float arcLenPerWire = fMMPerWire / fCosTheta;
218  float arcLenPerTick = arcLenPerWire / float(deltaTicks);
219  float numElectronsWire = fNumElectronsPerMM * arcLenPerWire;
220  float fracElecPerTick = numElectronsWire * arcLenPerTick / arcLenPerWire;
221 
222  // Make sure the waveform is zeroed
223  std::fill(fFFTTimeVec.begin(),fFFTTimeVec.end(),0.);
224 
225  // Now loop through the ticks and deposit charge
226  for(size_t tickIdx = startTick; tickIdx < endTick; tickIdx++)
227  fFFTTimeVec[tickIdx] = fracElecPerTick / asicGain;
228 
229  // Convolute with the response functions
230 // fSignalShapingService->Convolute(channel, fFFTTimeVec);
231  fFFT->convolute(fFFTTimeVec, response.getConvKernel(), timeOffset);
232 
233  // And now add this to the waveform in question
234  VectorFloat& waveform = waveforms[wireIdx];
235 
236  std::transform(waveform.begin(),waveform.end(),fFFTTimeVec.begin(),waveform.begin(),std::plus<float>());
237  }
238 
239  return;
240 }
241 
242 DEFINE_ART_CLASS_TOOL(FakeParticle)
243 } // namespace daq
icarusutil::TimeVec fFFTTimeVec
icarusutil::SignalShapingICARUSService * fSignalShapingService
double GetASICGain(unsigned int const channel) const
virtual unsigned int NumberTimeSamples() const =0
static constexpr Sample_t transform(Sample_t sample)
std::vector< size_t > fTickEndPoints
virtual const icarusutil::FrequencyVec & getConvKernel() const =0
std::vector< float > VectorFloat
Definition: IFakeParticle.h:51
FakeParticle(fhicl::ParameterSet const &pset)
Constructor.
~FakeParticle()
Destructor.
This provides an art tool interface definition for tools which can create &quot;fake&quot; particles to overlay...
std::vector< VectorFloat > ArrayFloat
Definition: IFakeParticle.h:53
const detinfo::DetectorProperties * fDetector
virtual void configure(const fhicl::ParameterSet &) override
Interface for configuring the particular algorithm tool.
std::vector< size_t > fWireEndPoints
std::vector< SigProcPrecision > TimeVec
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
FakeParticle class definiton.
The geometry of one entire detector, as served by art.
Definition: Geometry.h:181
IFakeParticle interface class definiton.
Definition: IFakeParticle.h:30
raw::ChannelID_t PlaneWireToChannel(WireID const &wireid) const
Returns the ID of the TPC channel connected to the specified wire.
Contains all timing reference information for the detector.
virtual void overlayFakeParticle(detinfo::DetectorClocksData const &clockData, ArrayFloat &waveforms) override
Creates a fake particle and overlays on the input fragment.
std::unique_ptr< icarus_signal_processing::ICARUSFFT< double >> FFTPointer
const geo::Geometry * fGeometry
const icarus_tool::IResponse & GetResponse(size_t channel) const
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
double sampling_rate(DetectorClocksData const &data)
Returns the period of the TPC readout electronics clock.
int ResponseTOffset(unsigned int const channel) const
art framework interface to geometry description
auto const detProp