LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
GeneratedEventTimestamp_plugin.cc
Go to the documentation of this file.
1 
10 // C/C++ standard libraries
11 #include <chrono>
12 #include <random>
13 
14 // framework libraries
19 
20 // Event generation namespace
21 namespace evgen {
22 
47  public:
48 
51 
52 
54  virtual art::Timestamp eventTimestamp(art::EventID const& id) override;
55 
57  virtual void rewind() override {}
58 
59 
60  private:
63 
64  }; // class GeneratedEventTimestamp
65 
66 
67 
68 
69 } // namespace evgen
70 
71 //------------------------------------------------------------------------------
72 //--- Implementation
73 //---
74 //---
75 
76 namespace evgen {
77  namespace details {
78 
79  //--------------------------------------------------------------------------
80  template <typename T>
81  struct Average {
82  using data_t = T;
83 
84  void clear() { fTotal = data_t(0); fN = 0U; }
85  void insert(data_t value) { fTotal += value; ++fN; }
86 
87  data_t n() const { return fN; }
88  data_t average() const { return fTotal / fN; }
89 
90  private:
91  unsigned int fN = 0U;
92  data_t fTotal = data_t(0);
93 
94  }; // class Average
95 
96 
97  //--------------------------------------------------------------------------
99  template <typename T>
100  auto discretize(T value, T period) {
101  auto const excess = value % period;
102  auto const base = value - excess;
103  return (excess < (period / T(2)))? base: base + period;
104  } // discretize()
105 
106 
107  //--------------------------------------------------------------------------
109  template <typename Clock, typename Unit>
111  public:
112 
115 
117  duration_t operator() () { return read_clock(); }
118 
120  static duration_t read_clock() { return timeFromEpoch(Clock::now()); }
121 
124  static duration_t currentOffsetFromEpoch();
125 
126  protected:
127 
129  template <typename TimeInterval>
130  static constexpr duration_t toDuration(TimeInterval dt)
131  {
132  return static_cast<duration_t>
133  (std::chrono::duration_cast<Unit>(dt).count());
134  }
135 
137  template <typename Rep, typename Period>
138  static constexpr auto periodToDuration()
139  { return toDuration(std::chrono::duration<Rep, Period>(1)); }
140 
142  template <typename TimePoint>
143  static duration_t timeFromEpoch(TimePoint t)
144  { return toDuration(t.time_since_epoch()); }
145 
146  }; // class TimeInUnitsBase<>
147 
148 
149  template <typename Clock, typename Duration>
151  -> duration_t
152  {
153  /*
154  * The plan is to compare the `Clock` we use with the system clock, which
155  * is guaranteed by the C++20 standard to refer to a well defined absolute
156  * time point (the UNIX epoch, January 1, 1970).
157  * Chances are that the resolution of the system clock is not as good as
158  * the one of `Clock`. If the difference between the two clocks is less
159  * than a few seconds, we attribute the difference to chance and we don't
160  * correct for it.
161  *
162  * Otherwise, the same time (almost!) is taken from the two clocks, and
163  * the difference in `Duration` units is used as a correction.
164  *
165  */
166  using clock_t = Clock;
167  using system_clock_t = std::chrono::system_clock;
168  using namespace std::chrono_literals;
169 
170  // no point in doing the exercise if we are already using the system clock
171  if (std::is_same<clock_t, system_clock_t>()) {
172  LOG_DEBUG("GeneratedEventTimestamp")
173  << "Using system clock for timestamp: no offset needed.";
174  return static_cast<duration_t>(0);
175  }
176 
177  auto clock_time = clock_t::now();
178  auto sys_clock_time = system_clock_t::now();
179 
180  // if the system clock is close to our clock, of if it is ahead of it,
181  // use no offset (the latter stems from the consideration that that the
182  // two clocks are equivalent although they suffer from some jitter)
183  if (
184  (timeFromEpoch(sys_clock_time) - timeFromEpoch(clock_time))
185  < toDuration(5s)
186  )
187  {
188  LOG_DEBUG("GeneratedEventTimestamp")
189  << "Offset with system clock is small ("
190  << (timeFromEpoch(sys_clock_time) - timeFromEpoch(clock_time))
191  << ", " << timeFromEpoch(sys_clock_time)
192  << " vs. " << timeFromEpoch(clock_time)
193  << "): no offset needed."
194  ;
195  return static_cast<duration_t>(0);
196  }
197 
198  //
199  // pick the largest of the resolutions for the comparison
200  //
201  using clock_period_t = typename clock_t::period;
202  using system_clock_period_t = system_clock_t::period;
203  using largest_period_t = std::conditional_t<
204  (
205  clock_period_t::num * system_clock_period_t::den
206  > system_clock_period_t::num * clock_period_t::den
207  ),
208  clock_period_t,
209  system_clock_period_t
210  >;
211  // this is the period expressed in the Duration unit
212  constexpr auto largest_period
213  = periodToDuration<typename clock_t::rep, largest_period_t>();
214 
215  //
216  // compare and round
217  //
218  constexpr unsigned int times = 10U; // average 10 samples
219  Average<duration_t> offset;
220  for (unsigned int i = 0; i < times; ++i) {
221 
222  offset.insert
223  (timeFromEpoch(sys_clock_time) - timeFromEpoch(clock_time));
224  clock_time = clock_t::now();
225  sys_clock_time = system_clock_t::now();
226 
227  } // for
228 
229  LOG_DEBUG("GeneratedEventTimestamp")
230  << "System clock period: "
231  << periodToDuration<typename clock_t::rep, system_clock_period_t>()
232  << "\nUser clock period: "
233  << periodToDuration<typename clock_t::rep, clock_period_t>()
234  << "\nOffset: " << offset.average()
235  << " (rounded to: " << largest_period << ")"
236  ;
237 
238  // round off the offset with one "largest period"
239  return discretize(offset.average(), largest_period);
240 
241  } // TimeInUnitsBase<>::currentOffsetFromEpoch()
242 
243 
244  //--------------------------------------------------------------------------
247  template <typename Clock, typename Duration, typename = void>
248  class TimeInUnits: public TimeInUnitsBase<Clock, Duration> {};
249 
250 
251  // Implementation of the random-gap-filling version
252  template <typename Clock, typename Duration>
253  class TimeInUnits<Clock, Duration, typename std::enable_if<
254  (Clock::period::num * Duration::period::den > Duration::period::num * Clock::period::den)
255  >::type
256  >
257  : public TimeInUnitsBase<Clock, Duration>
258  {
260  using typename Base_t::duration_t;
261 
262  // if the period of the clock is larger than the unit we are requested,
263  // there will be some padding
264  using ClockPeriod = typename Clock::period;
265  using ReqPeriod = typename Duration::period;
266  // requested clock / unit:
267  using PeriodRatio = std::ratio<
268  ClockPeriod::num * ReqPeriod::den, ReqPeriod::num * ClockPeriod::den
269  >;
270  static constexpr intmax_t paddingfactor
271  = PeriodRatio::num / PeriodRatio::den; // > 1 enforced by enable_if<>
272 
273  public:
274  TimeInUnits(): engine(), flat(0, paddingfactor - 1) {}
275 
277  duration_t operator() ()
278  { return Base_t::read_clock() + flat(engine); }
279 
280  private:
281  std::default_random_engine engine;
282  std::uniform_int_distribution<duration_t> flat;
283  }; // class TimeInUnits<> (padded)
284 
285 
287  <std::chrono::high_resolution_clock, std::chrono::nanoseconds>;
288 
289  //--------------------------------------------------------------------------
290 
291  } // namespace details
292 } // namespace evgen
293 
294 
295 //------------------------------------------------------------------------------
297  (fhicl::ParameterSet const& pset)
300 {
301 
302  mf::LogInfo("GeneratedEventTimestamp")
303  << "Timestamp plugin: timestamp from local clock time in nanoseconds";
304  if (fOffsetFromEpoch != 0) {
305  LOG_TRACE("GeneratedEventTimestamp")
306  << " Time offset from epoch: " << fOffsetFromEpoch << " ns";
307  }
308 
309 } // evgen::GeneratedEventTimestamp::GeneratedEventTimestamp()
310 
311 
312 //------------------------------------------------------------------------------
314  (art::EventID const& id)
315 {
316  // obtain from the high resolution clock the current time, from the "epoch",
317  // in nanoseconds; if the clock is less precise than the nanosecond,
318  // the precision gap is filled with randomness
319  details::ns_clock_t get_time;
320 
321  const long long int now_ns = fOffsetFromEpoch + get_time();
322 
323  // convert into a timestamp
324  art::Timestamp ts(now_ns);
325 
326  mf::LogTrace("GeneratedEventTimestamp")
327  << "Generated time stamp: " << ts.value() << " for event " << id;
328 
329  return ts;
330 } // evgen::GeneratedEventTimestamp::eventTimestamp()
331 
332 // make art aware that we have a plugin
334 
Float_t s
Definition: plot.C:23
std::ratio< ClockPeriod::num *ReqPeriod::den, ReqPeriod::num *ClockPeriod::den > PeriodRatio
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
Float_t den
Definition: plot.C:37
virtual void rewind() override
Resets the status; since this plug in is stateless, this is a no-op.
auto discretize(T value, T period)
Returns the multiple of period closest to value.
Plugin to assign an empty event a time stamp from the clock.
STL namespace.
static constexpr auto periodToDuration()
Returns the duration (duration_t) of a period type.
constexpr TimeValue_t value() const
Definition: Timestamp.h:24
Class reading a Clock and converting the value to a specific Unit.
static duration_t read_clock()
Reads and returns the current time the clock.
GeneratedEventTimestamp(fhicl::ParameterSet const &pset)
Constructor: nothing specific.
std::uniform_int_distribution< duration_t > flat
art::TimeValue_t const fOffsetFromEpoch
Offset to be added to the chosen clock to get an absolute time.
#define DEFINE_ART_EMPTYEVENTTIMESTAMP_PLUGIN(klass)
static constexpr duration_t toDuration(TimeInterval dt)
Converts a std::chrono::duration into our duration metric.
std::uint64_t TimeValue_t
Definition: Timestamp.h:7
std::string value(boost::any const &)
virtual art::Timestamp eventTimestamp(art::EventID const &id) override
Returns the time stamp for the specified event.
art::TimeValue_t duration_t
Type of the time duration as returned by this class.
#define LOG_DEBUG(id)
art::TimeValue_t duration_t
Type of the time duration as returned by this class.
MaybeLogger_< ELseverityLevel::ELsev_success, true > LogTrace
Event Generation using GENIE, cosmics or single particles.
static duration_t timeFromEpoch(TimePoint t)
Returns the time elapsed from the epoch to t.
#define LOG_TRACE(id)