All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CustomPulseFunction.h
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/Algorithms/CustomPulseFunction.h
3  * @brief Pulse from one photoelectron fully defined by the configuration.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date March 18, 2020
6  *
7  * This library is header only.
8  *
9  */
10 
11 #ifndef ICARUSCODE_PMT_ALGORITHMS_CUSTOMPULSEFUNCTION_H
12 #define ICARUSCODE_PMT_ALGORITHMS_CUSTOMPULSEFUNCTION_H
13 
14 // library header
16 
17 // LArSoft libraries
20 
21 // ROOT
22 #include "TFormula.h"
23 
24 // C++ standard library
25 #include <stdexcept> // std::runtime_error
26 #include <ostream> // std::ostream
27 #include <algorithm> // std::set_difference()
28 #include <vector>
29 #include <set>
30 #include <iterator> // std::inserter
31 #include <string>
32 #include <utility> // std::forward(), std::pair
33 #include <cmath> // std::exp()
34 
35 
36 // -----------------------------------------------------------------------------
37 namespace icarus::opdet {
38  template <typename T> class CustomPulseFunction;
39 }
40 
41 // -----------------------------------------------------------------------------
42 /**
43  * @brief Describes the waveform from a single photoelectron.
44  * @tparam Time type of time unit to be used
45  *
46  * This functor (class behaving like a function) describes the shape of the
47  * response to a single photoelectron as a custom shape.
48  *
49  * The shape is a formula interpreted via ROOT's `TFormula`.
50  * It is assumed to start at time `0.0` (in `Time` units).
51  *
52  */
53 template <typename T>
56 {
58 
59  public:
60  /// Type for ADC counts (floating point).
61  using ADCcount = typename Base_t::ADCcount;
62 
63  using Time = typename Base_t::Time; ///< Type of time being used.
64 
65  using ParameterValue_t = double; ///< Type of value for parameters (TFormula).
66 
67  /// Type of parameter name/value definition.
68  using NameAndValue_t = std::pair<std::string, ParameterValue_t>;
69 
70  /// Type of list of all function parameters.
71  using PulseParameters_t = std::vector<NameAndValue_t>;
72 
73 
74  // @{
75  /**
76  * @brief Constructor: chooses shape and the values of its parameters.
77  * @param expression mathematical expression of pulse shape
78  * @param parameters the list of named parameters and their values
79  * @param peakTime expression of time of the maximum amplitude of the shape
80  *
81  * The shape mathematical `expression` must be compatible with ROOT 6
82  * `TFormula`.
83  * The formula must have a single independent variable, time measured in
84  * the `Time` scale. The value `0` of that time corresponds to when the
85  * photoelectron is emitted from the photocathode.
86  *
87  * The peak time argument is used to report when the peak happens.
88  * It would be possible to compute it in a general way, but the implementation
89  * of that functionality is complicate enough to get it right and fast, that
90  * it's better to rely on user's knowledge. The value may be either a number
91  * or a string. The number (`Time`) directly represents the time of the
92  * peak, while the expression is another `TFormula` expression that can use
93  * the same `parameters` as the shape `expression`, so that for example it is
94  * possible to indicate the peak time of a shape like `exp(-(x - [mu])**2)`
95  * as `[mu]`. In this case, the peak time expression must use no variable.
96  *
97  */
99  std::string const& expression,
100  PulseParameters_t const& parameters,
101  Time peakTime
102  );
104  std::string const& expression,
105  PulseParameters_t const& parameters,
106  std::string const& peakTime
107  );
108  // @}
109 
110  /// @{
111  /// @name Parameter accessors.
112 
113  /// Returns the value of the parameter specified by `name`.
114  ParameterValue_t parameter(std::string const& name) const;
115 
116  /// @}
117 
118 
119  private:
120 
121  /// Record of collected information on the pulse shape.
122  struct PulseStats_t {
123  bool negativePulse; ///< Whether the pulse is considered negative.
124  Time peakTime; ///< Time of the pulse peak.
125  ADCcount peakAmplitude; ///< Pulse amplitude at peak time.
126  }; // PulseStats_t
127 
128 
129  TFormula fFormula; ///< Formula of the shape.
130 
131  PulseStats_t fStats; ///< Collected information about the pulse.
132 
133 
134  /// Constructor implementation.
136  std::string const& expression,
137  PulseParameters_t const& parameters
138  );
139 
140 
141  // --- BEGIN -- Interface implementation -------------------------------------
142  /**
143  * @brief Evaluates the pulse at the given time.
144  * @param time time to evaluate the shape at
145  *
146  * The scale of the time is defined by the transition time passed
147  * at construction.
148  */
149  virtual ADCcount doEvaluateAt(Time time) const override
150  { return ADCcount::castFrom(fFormula.Eval(static_cast<double>(time))); }
151 
152  /// Returns the time at which the first peak is found.
153  virtual Time doPeakTime() const override { return fStats.peakTime; }
154 
155  /// Returns the amplitude of the first peak in ADC counts.
156  virtual ADCcount doPeakAmplitude() const override
157  { return fStats.peakAmplitude; }
158 
159  /**
160  * @brief Prints on stream the parameters of this shape.
161  * @param out the stream to write into
162  * @param indent indentation string, prepended to all lines except first
163  * @param indentFirst indentation string prepended to the first line
164  */
165  virtual void doDump(
166  std::ostream& out,
167  std::string const& indent, std::string const& firstIndent
168  ) const override;
169 
170  // --- END -- Interface implementation -------------------------------------
171 
172 
173  /// Extracts statistics from pulse shape.
174  PulseStats_t extractStats(Time peakTime) const;
175 
176  /// Returns a message about parameters in `formula` missing in `parameters`.
177  /// @return a message about missing parameters, empty if none
178  static std::string checkMissingParameters
179  (TFormula const& formula, PulseParameters_t const& parameters);
180 
181  /// Returns a message about `parameters` which are not in `formula`.
182  /// @return a message about excess parameters, empty if none
183  static std::string checkExcessParameters
184  (TFormula const& formula, PulseParameters_t const& parameters);
185 
186  /// Sets the value of the `parameters` of `formula`.
187  static void setParameters
188  (TFormula& formula, PulseParameters_t const& parameters);
189 
190 }; // class icarus::opdet::CustomPulseFunction<>
191 
192 
193 // -----------------------------------------------------------------------------
194 // --- template implementation
195 // -----------------------------------------------------------------------------
196 template <typename T>
198  std::string const& expression,
199  PulseParameters_t const& parameters,
200  Time peakTime
201  )
202  : CustomPulseFunction(expression, parameters)
203 {
204  fStats = extractStats(peakTime);
205 } // icarus::opdet::CustomPulseFunction<>::CustomPulseFunction(Time)
206 
207 
208 // -----------------------------------------------------------------------------
209 template <typename T>
211  std::string const& expression,
212  PulseParameters_t const& parameters,
213  std::string const& peakTime
214  )
215  : CustomPulseFunction(expression, parameters)
216 {
217  TFormula peakExpr("CustomPulseFunctionPeak", peakTime.c_str(), false);
218 
219  // excess parameters are allowed because they might belong to `expression`
220  std::string const msg = checkMissingParameters(peakExpr, parameters);
221  if (!msg.empty()) throw std::runtime_error("CustomPulseFunction:" + msg);
222 
223  setParameters(peakExpr, parameters);
224  fStats = extractStats(Time{ peakExpr.Eval(0.0) }); // dummy value
225 
226 } // icarus::opdet::CustomPulseFunction<>::CustomPulseFunction(string)
227 
228 
229 // -----------------------------------------------------------------------------
230 template <typename T>
232  std::string const& expression,
233  PulseParameters_t const& parameters
234  )
235  : fFormula{
236  "CustomPulseFunction", // fixed name, hope ROOT does not mess it up...
237  expression.c_str(),
238  false // addToGlobList
239  }
240 {
241  std::string const msg = checkMissingParameters(fFormula, parameters)
242  + checkExcessParameters(fFormula, parameters);
243  if (!msg.empty()) throw std::runtime_error("CustomPulseFunction:" + msg);
244 
245  setParameters(fFormula, parameters);
246 } // icarus::opdet::CustomPulseFunction<>::CustomPulseFunction(impl)
247 
248 
249 // -----------------------------------------------------------------------------
250 template <typename T>
252  (Time peakTime) const -> PulseStats_t
253 {
254  PulseStats_t stats;
255 
256  stats.peakTime = peakTime;
257  stats.peakAmplitude = (*this)(peakTime);
258  stats.negativePulse = stats.peakAmplitude < ADCcount{ 0 };
259 
260  return stats;
261 } // icarus::opdet::CustomPulseFunction<>::extractStats()
262 
263 
264 // -----------------------------------------------------------------------------
265 template <typename T>
267  std::ostream& out,
268  std::string const& indent, std::string const& firstIndent
269  ) const
270 {
271  out
272  << firstIndent << "Custom pulse shape: " << fFormula.GetTitle()
273  << "\n" << indent << " peak " << fStats.peakAmplitude
274  << " at " << fStats.peakTime
275  ;
276  if (fFormula.GetNpar() > 0) {
277  out << "\n" << indent << "Parameters (" << fFormula.GetNpar() << "):";
278  for (auto const iPar: util::counter(fFormula.GetNpar())) {
279  out << "\n" << indent << " [" << fFormula.GetParName(iPar) << "] = "
280  << fFormula.GetParameter(iPar);
281  } // for
282  } // if parameters
283  out << '\n';
284 } // icarus::opdet::CustomPulseFunction<>::doDump()
285 
286 
287 // -----------------------------------------------------------------------------
288 template <typename T>
290  (TFormula const& formula, PulseParameters_t const& parameters)
291 {
292  std::set<std::string> required;
293  for (auto const iPar: util::counter(formula.GetNpar()))
294  required.insert(formula.GetParName(iPar));
295  std::set<std::string> offered;
296  for (auto const& nameAndValue: parameters)
297  offered.insert(nameAndValue.first);
298 
299  std::set<std::string> missing;
300  std::set_difference(
301  required.cbegin(), required.cend(), offered.cbegin(), offered.cend(),
302  std::inserter(missing, missing.begin())
303  );
304  if (missing.empty()) return {}; // success
305 
306  std::string msg
307  { "\n * " + std::to_string(missing.size()) + " parameters missing:" };
308  for (auto const& name: missing) msg += "\n - '" + name + "'";
309 
310  return msg;
311 
312 } // icarus::opdet::CustomPulseFunction<>::checkMissingParameters()
313 
314 
315 // -----------------------------------------------------------------------------
316 template <typename T>
318  (TFormula const& formula, PulseParameters_t const& parameters)
319 {
320  std::set<std::string> offered;
321  for (auto const& nameAndValue: parameters)
322  offered.insert(nameAndValue.first);
323  std::set<std::string> required;
324  for (auto const iPar: util::counter(formula.GetNpar()))
325  required.insert(formula.GetParName(iPar));
326 
327  std::set<std::string> excess;
328  std::set_difference(
329  offered.cbegin(), offered.cend(), required.cbegin(), required.cend(),
330  std::inserter(excess, excess.begin())
331  );
332  if (excess.empty()) return {}; // success
333 
334  std::string msg
335  { "\n * " + std::to_string(excess.size()) + " unused parameters:" };
336  for (auto const& name: excess) msg += "\n - '" + name + "'";
337 
338  return msg;
339 
340 } // icarus::opdet::CustomPulseFunction<>::checkExcessParameters()
341 
342 
343 // -----------------------------------------------------------------------------
344 template <typename T>
346  (TFormula& formula, PulseParameters_t const& parameters)
347 {
348  for (auto const& [ name, value ]: parameters) {
349  auto const iPar = formula.GetParNumber(name.c_str());
350  if (iPar < 0) continue;
351  formula.SetParameter(iPar, value);
352  } // for
353 } // icarus::opdet::CustomPulseFunction<>::setParameters()
354 
355 
356 // -----------------------------------------------------------------------------
357 
358 #endif // ICARUSCODE_PMT_ALGORITHMS_CUSTOMPULSEFUNCTION_H
static std::string checkMissingParameters(TFormula const &formula, PulseParameters_t const &parameters)
static std::string checkExcessParameters(TFormula const &formula, PulseParameters_t const &parameters)
Record of collected information on the pulse shape.
virtual ADCcount doEvaluateAt(Time time) const override
Evaluates the pulse at the given time.
virtual ADCcount doPeakAmplitude() const override
Returns the amplitude of the first peak in ADC counts.
TFormula fFormula
Formula of the shape.
static void setParameters(TFormula &formula, PulseParameters_t const &parameters)
Sets the value of the parameters of formula.
CustomPulseFunction(std::string const &expression, PulseParameters_t const &parameters, Time peakTime)
Constructor: chooses shape and the values of its parameters.
std::pair< std::string, ParameterValue_t > NameAndValue_t
Type of parameter name/value definition.
Abstract interface of shape of a pulse from one photoelectron.
bool negativePulse
Whether the pulse is considered negative.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:285
std::vector< NameAndValue_t > PulseParameters_t
Type of list of all function parameters.
ParameterValue_t parameter(std::string const &name) const
Returns the value of the parameter specified by name.
Time peakTime() const
Returns the time at which the first peak is found.
Interface for a function describing a pulse from a photoelectron.
ADCcount peakAmplitude
Pulse amplitude at peak time.
Test of util::counter and support utilities.
typename Base_t::ADCcount ADCcount
Type for ADC counts (floating point).
PulseStats_t fStats
Collected information about the pulse.
virtual Time doPeakTime() const override
Returns the time at which the first peak is found.
Dimensioned variables related to electronics.
Describes the waveform from a single photoelectron.
std::string to_string(WindowPattern const &pattern)
double ParameterValue_t
Type of value for parameters (TFormula).
typename Base_t::Time Time
Type of time being used.
then echo fcl name
temporary value
PulseStats_t extractStats(Time peakTime) const
Extracts statistics from pulse shape.
util::quantities::counts_f ADCcount
Type for ADC counts (floating point).
virtual void doDump(std::ostream &out, std::string const &indent, std::string const &firstIndent) const override
Prints on stream the parameters of this shape.