All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CustomPulseFunctionTool_tool.cc
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/CustomPulseFunctionTool_tool.cc
3  * @brief Toolization of `icarus::opdet::AsymGaussPulseFunction<nanosecond>`.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date March 17, 2020
6  * @see `icaruscode/PMT/Algorithms/CustomPulseFunction.h`
7  *
8  * This is an implementation of tool interface
9  * `icarus::opdet::SinglePhotonPulseFunctionTool`.
10  */
11 
12 
13 // ICARUS libraries
16 
17 // LArSoft libraries
19 #include "lardataalg/Utilities/quantities_fhicl.h" // nanoseconds from FHiCL
20 
21 // framework libraries
22 #include "art/Utilities/ToolConfigTable.h"
23 #include "art/Utilities/ToolMacros.h"
24 
25 // framework libraries
26 #include "messagefacility/MessageLogger/MessageLogger.h"
27 #include "fhiclcpp/types/OptionalDelegatedParameter.h"
28 #include "fhiclcpp/types/Atom.h"
29 #include "fhiclcpp/ParameterSet.h"
30 
31 // C/C++ standard libraries
32 #include <memory> // std::unique_ptr()
33 #include <cassert>
34 
35 
36 //------------------------------------------------------------------------------
37 namespace icarus::opdet { struct CustomPulseFunctionTool; }
38 /**
39  * @brief Creates a `CustomPulseFunction` pulse shape.
40  * @see `icarus::opdet::SinglePhotonPulseFunctionTool`
41  *
42  * This tool creates a `icarus::opdet::CustomPulseFunction<nanosecond>`
43  * function to describe an arbitrary pulse shape.
44  *
45  * See `icarus::opdet::CustomPulseFunction` for the details of the function.
46  *
47  *
48  * Configuration
49  * --------------
50  *
51  * Run `lar --print-description CustomPulseFunctionTool` (or read `Config`
52  * data structure) for a short explanation of the meaning of the parameters.
53  *
54  * * **ShapeFormula** (string, mandatory): an expression for the pulse shape;
55  * for example: `[A] * exp(-0.5*((x - [mu])/(sqrt2*[sigma]))**2)` is an
56  * extended way to describe a Gaussian pulse; the syntax is mostly C++ with a
57  * few ROOT extensions (see ROOT 6 `TFormula` documentation); `x` variable
58  * represents the time in nanoseconds, with `x = 0` the time of emission of
59  * the photoelectron;
60  * * **PeakTime** (string, mandatory): evaluates to the time at which the pulse
61  * has its peak, in nanoseconds; the format can be an expression in the same
62  * fashion as `ShapeFormula` and it can use any of the parameters in
63  * `ShapeFormula`, but no extra parameters and with no variable, for example
64  * `[mu]` for the Gaussian pulse peak; or it can be a time, for example
65  * `55.1 ns`;
66  * * **Parameters** (string): a table with one entry for each parameter of the
67  * pulse shape, and their numerical value (no unit is used); for example,
68  * `mu: 55.1 sigma: 2.4 A: -10.1` sets the parameters of the Gaussian shape
69  * in the previous example. This parameter must contain one entry for each
70  * parameter in `ShapeFormula` and no extra values; if `ShapeFormula` has
71  * no parameters, the `Parameters` table can be omitted.
72  *
73  * @note Because of the limitations of FHiCL language in `Parameters`
74  * specification, the names of the parameters need to be simple (e.g.
75  * `[mu]` rather than `[#mu]`).
76  */
79 {
80 
81  /// Configuration parameters.
82  struct Config {
83 
84  using Name = fhicl::Name;
85  using Comment = fhicl::Comment;
86 
87  fhicl::Atom<std::string> ShapeFormula {
88  Name("ShapeFormula"),
89  Comment("formuls for the pulse shape, in ROOT TFormula format")
90  // mandatory
91  };
92  fhicl::Atom<std::string> PeakTime {
93  Name("PeakTime"),
94  Comment("time at which the pulse peaks [ns]")
95  // mandatory
96  };
97  fhicl::OptionalDelegatedParameter Parameters {
98  Name("Parameters"),
99  Comment("collection of parameter names and their numerical values")
100  };
101 
102  }; // struct Config
103 
104 
105  /// Tool parameter configuration.
106  using Parameters = art::ToolConfigTable<Config>;
107 
108  /// Constructor: sets the configuration.
110  : fPulseFunction(makePulseFunction(config())) {}
111 
112 
113  private:
114 
115  // --- BEGIN -- Virtual interface --------------------------------------------
116 
117  /// Returns the function that was created at construction time.
118  virtual std::unique_ptr<PulseFunction_t> doGetPulseFunction() override
119  { return std::move(fPulseFunction); }
120 
121  // --- END -- Virtual interface ----------------------------------------------
122 
123  /// Function stored while waiting to be delivered.
124  std::unique_ptr<PulseFunction_t> fPulseFunction;
125 
126 
127  /// Creates and returns a pulse function with the specified configuration.
128  static std::unique_ptr<PulseFunction_t> makePulseFunction
129  (Config const& config);
130 
131 
132 }; // icarus::opdet::CustomPulseFunctionTool
133 
134 
135 //------------------------------------------------------------------------------
136 //--- icarus::opdet::CustomPulseFunctionTool implementation
137 //------------------------------------------------------------------------------
139  (Config const& config) -> std::unique_ptr<PulseFunction_t>
140 {
141 
143 
144  std::string const& expression = config.ShapeFormula();
145 
146  // collect the parameters
147  fhicl::ParameterSet configuredParameters; // will stay empty if not present
148  config.Parameters.get_if_present(configuredParameters);
149  MyFunction_t::PulseParameters_t parameters;
150  for (auto const& parName: configuredParameters.get_names())
151  parameters.emplace_back(parName, configuredParameters.get<double>(parName));
152 
153  std::string const& peakTimeStr = config.PeakTime();
154 
155  try {
156  // try to convert `peakTimeStr` into a nanosecond quantity
157  return std::make_unique<MyFunction_t>(
158  expression,
159  parameters,
160  util::quantities::makeQuantity<nanoseconds>(peakTimeStr, true)
161  );
162  }
163  catch (util::quantities::ValueError const&) {}
165 
166  mf::LogDebug("CustomPulseFunctionTool")
167  << "Parameter PeakTime ('" << peakTimeStr
168  << "') does not seem to be a nanosecond quantity; will try as formula."
169  ;
170 
171  return std::make_unique<MyFunction_t>(expression, parameters, peakTimeStr);
172 
173 } // icarus::opdet::CustomPulseFunctionTool::makePulseFunction()
174 
175 
176 //------------------------------------------------------------------------------
177 DEFINE_ART_CLASS_TOOL(icarus::opdet::CustomPulseFunctionTool)
178 
179 
180 //------------------------------------------------------------------------------
181 
virtual std::unique_ptr< PulseFunction_t > doGetPulseFunction() override
Returns the function that was created at construction time.
static std::unique_ptr< PulseFunction_t > makePulseFunction(Config const &config)
Creates and returns a pulse function with the specified configuration.
Creates a CustomPulseFunction pulse shape.
std::unique_ptr< PulseFunction_t > fPulseFunction
Function stored while waiting to be delivered.
CustomPulseFunctionTool(Parameters const &config)
Constructor: sets the configuration.
Dimensioned variables representing electromagnetic quantities.
art::ToolConfigTable< Config > Parameters
Tool parameter configuration.
Tool to create a pulse function for PMT single photon response.
String representing a quantity has spurious characters after the number.
Definition: quantities.h:1108
Creates a PhotoelectronPulseFunction pulse shape.
Pulse from one photoelectron fully defined by the configuration.
Utilities to read and write quantities in FHiCL configuration.
BEGIN_PROLOG vertical distance to the surface Name
Describes the waveform from a single photoelectron.
String representing a quantity has an invalid number.
Definition: quantities.h:1104