All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FindAllP.h
Go to the documentation of this file.
1 /**
2  * @file FindAllP.h
3  * @brief More association queries
4  * @date December 18th, 2014
5  * @author Gianluca Petrillo (petrillo@fnal.gov)
6  *
7  * The code here is implementation details, living in lar::util::details
8  * namespace.
9  */
10 
11 #ifndef FINDALLP_H
12 #define FINDALLP_H 1
13 
14 // C/C++ standard libraries
15 #include <climits> // CHAR_BIT
16 #include <stdexcept> // std::out_of_range
17 #include <vector>
18 #include <unordered_map>
19 #include <functional> // std::hash<>
20 
21 
22 // framework libraries
23 #include "messagefacility/MessageLogger/MessageLogger.h"
24 #include "canvas/Utilities/Exception.h"
25 #include "canvas/Utilities/InputTag.h"
26 #include "canvas/Persistency/Common/Ptr.h"
27 #include "canvas/Persistency/Common/Assns.h"
28 #include "canvas/Persistency/Provenance/ProductID.h"
29 #include "art/Framework/Principal/Event.h"
30 
31 /// LArSoft-specific namespace
32 namespace lar {
33 
34  /// LArSoft utility namespace
35  namespace util {
36 
37  /// LArSoft utility implementation details
38  namespace details {
39 
40  /// Hash functions for art and larsoft objects
41  template <typename T>
42  struct hash {
43  using result_type = size_t;
44  using argument_type = T;
45 
46  result_type operator() (argument_type const& v) const;
47 
48  }; // class hash<>
49 
50 
51 
52 
53  /** **********************************************************************
54  * @brief A class holding many associations between objects
55  * @tparam Source type of the object we use as query key (indexing object)
56  * @tparam Dest type of the object we query for
57  *
58  * This class is conceptually related to the query object FindOneP.
59  * This object is a cache of possible query results of the type:
60  * which Dest object is associated to this specific Src object?
61  * The cache is structured so that only one Dest object is known for each
62  * Src.
63  */
64  template <typename Source, typename Dest>
66  public:
67  using Source_t = Source;
68  using Dest_t = Dest;
69 
70  using SourcePtr_t = art::Ptr<Source_t>;
71  using DestPtr_t = art::Ptr<Dest_t>;
72 
73  /// type for a cache of dest products for a given source product ID
74  using InProductCache_t = std::vector<DestPtr_t>;
75 
76  /// type of hash functor for the cache
78 
79  /// type for the complete cache, keyed by source product ID
80  using Cache_t
81  = std::unordered_map<art::ProductID, InProductCache_t, Hash_t>;
82 
83  Cache_t AssnCache; ///< association cache, keyed by product ID and index
84 
85 
86  /// Constructor: an empty cache
87  UniqueAssociationCache() = default;
88 
89  /**
90  * @brief Returns the specified element of the cache
91  * @param src art pointer to the object we want the association of
92  * @return the requested element, or a null pointer if not found
93  */
94  DestPtr_t operator[] (SourcePtr_t const& src) const
95  { return AssnCache[src.id()][src.key()]; }
96 
97  /// Empties the cache
98  void clear() { AssnCache.clear(); }
99 
100  size_t NProductIDs() const { return AssnCache.size(); }
101 
102  }; // class UniqueAssociationCache<>
103 
104 
105  /** **********************************************************************
106  * @brief Query object reading *all* the associations between two classes
107  * @tparam Source type of the object we use as query key (indexing object)
108  * @tparam Dest type of the object we query for
109  *
110  * When assigned an event, this object reads all the associations from
111  * Source type classes to Dest type classes in the event, and stores
112  * their information in a map to track a Dest object from its Source one.
113  * In fact, it assumes that only one Dest object is associated,
114  * event-wise, to each single Source object.
115  */
116  template <typename Source, typename Dest>
117  class FindAllP {
118  public:
119  using Source_t = Source;
120  using Dest_t = Dest;
121 
122  /// Default constructor: empty query, read information with Read()
123  FindAllP() = default;
124 
125  /// Constructor: reads all associations from the specified event
126  FindAllP(art::Event& event): FindAllP() { Read(event); }
127 
128  /// Constructor: reads one association from the specified event
129  FindAllP(art::Event& event, art::InputTag assnTag): FindAllP()
130  { Read(event, assnTag); }
131 
132 
133  /**
134  * @brief Returns the object associated to the specified one
135  * @param src a art pointer to the source object
136  * @return a pointer to the associated object, or a null pointer if none
137  */
138  art::Ptr<Dest_t> const& operator[] (art::Ptr<Dest_t> const& src) const;
139 
140 
141  /// Returns whether there are associations from objects in product id
142  bool hasProduct(art::ProductID const& id) const;
143 
144  /// Returns if there are associations from objects with product as ptr
145  bool hasProduct(art::Ptr<Source_t> const& ptr) const;
146 
147  /**
148  * @brief Reads all the associations from the event
149  * @throw art::Exception if multiple dest objects are found for one
150  * source object
151  */
152  unsigned int Read(art::Event& event);
153 
154  /**
155  * @brief Reads the specified association from the event
156  * @param event the event to read the associations from
157  * @param assnTag the input tag for the association
158  * @throw art::Exception if multiple dest objects are found for one
159  * source object
160  *
161  * The input tag for the association is usually simply a string with the
162  * name of the module that produced the association, and often the same
163  * module has also produced the source objects as well.
164  */
165  unsigned int Read(art::Event& event, art::InputTag const& assnTag);
166 
167 
168  /**
169  * @brief Reads the specified association from the event
170  * @param event the event to read the associations from
171  * @param assnTag the input tag for the association
172  * @throw art::Exception if multiple dest objects are found for one
173  * source object
174  *
175  * The existing associations already in cache are not removed.
176  *
177  * The input tag for the association is usually simply a string with the
178  * name of the module that produced the association, and often the same
179  * module has also produced the source objects as well.
180  */
181  unsigned int Add(art::Event& event, art::InputTag const& assnTag);
182 
183 
184  protected:
185  using Assns_t = art::Assns<Source_t, Dest_t>;
186 
187  /// Type of the cache
189 
190  Cache_t cache; ///< set of associations, keyed by product ID and key
191 
192  /// Adds all associations in the specified handle; returns their number
193  unsigned int Merge(art::Handle<Assns_t>& handle);
194  }; // class FindAllP<>
195 
196 
197 
198 
199  /** **********************************************************************
200  * @brief Resizes a vector to a size power of 2, with a minimum size
201  * @param v the vector to be resized
202  * @param min_size the minimal size of the vector
203  *
204  * The vector v is resized, any new element is default-initialized.
205  * The target size is the smallest power of 2 not smaller than min_size,
206  * or 0 if min_size is 0.
207  */
208  template <typename T>
209  void ResizeToPower2(std::vector<T>& v, size_t min_size);
210 
211 
212  } // namespace details
213  } // namespace util
214 } // namespace lar
215 
216 
217 
218 //******************************************************************************
219 //*** Template implementation
220 //******************************************************************************
221 
222 namespace lar {
223  namespace util {
224  namespace details {
225  //------------------------------------------------------------------------
226  //--- FindAllP
227 
228  template <typename Source, typename Dest>
229  auto FindAllP<Source, Dest>::operator[]
230  (art::Ptr<Dest_t> const& src) const -> art::Ptr<Dest_t> const&
231  {
232  // we expect a missing match to be exceptional
233  try {
234  return cache.AssnCache.at(src.id()).at(src.key());
235  }
236  catch (std::out_of_range) {
237  return {};
238  }
239  } // FindAllP<>::operator[]
240 
241 
242  template <typename Source, typename Dest>
244  (art::ProductID const& id) const
245  { return cache.AssnCache.count(id) > 0; }
246 
247 
248  template <typename Source, typename Dest>
250  (art::Ptr<Source_t> const& ptr) const
251  { return hasProduct(ptr.id()); }
252 
253 
254  template <typename Source, typename Dest>
255  unsigned int FindAllP<Source, Dest>::Read
256  (art::Event& event)
257  {
258 
259  // read all the associations between source and destination class types
260  //std::vector<art::Handle<Assns_t>> assns_list;
261  //event.getManyByType(assns_list);
262  auto assns_list = event.getMany<Assns_t>();
263 
264  MF_LOG_DEBUG("FindAllP") << "Read(): read " << assns_list.size()
265  << " association sets";
266 
267  unsigned int count = 0;
268  // parse all the associations, and translate them into a local cache
269  for (art::Handle<Assns_t> handle: assns_list)
270  count += Merge(handle);
271 
272  MF_LOG_DEBUG("FindAllP") << "Read " << count << " associations for "
273  << cache.NProductIDs() << " product IDs";
274 
275  return count;
276  } // FindAllP::Read(Event)
277 
278 
279 
280  template <typename Source, typename Dest>
281  unsigned int FindAllP<Source, Dest>::Read
282  (art::Event& event, art::InputTag const& assnTag)
283  {
284  cache.clear();
285  return Add(event, assnTag);
286  } // FindAllP::Read(Event, InputTag)
287 
288 
289 
290  template <typename Source, typename Dest>
291  unsigned int FindAllP<Source, Dest>::Add
292  (art::Event& event, art::InputTag const& assnTag)
293  {
294 
295  // read the association between source and destination class types
296  art::Handle<Assns_t> handle;
297  if (!event.getByLabel(assnTag, handle)) {
298  throw art::Exception(art::errors::ProductNotFound)
299  << "no association found with input tag '" << assnTag << "'";
300  }
301 
302  return Merge(handle);
303  } // FindAllP::Add(Event, InputTag)
304 
305 
306  template <typename Source, typename Dest>
307  unsigned int FindAllP<Source, Dest>::Merge
308  (art::Handle<Assns_t>& handle)
309  {
310  // product ID of the last source object; initialized invalid
311  art::ProductID LastProductID = art::Ptr<Source_t>().id();
312  typename Cache_t::InProductCache_t const* AssnsList = nullptr;
313 
314  unsigned int count = 0;
315 
316  MF_LOG_DEBUG("FindAllP") << "Merge(): importing " << handle->size()
317  << " associations from " << handle.provenance();
318 
319  for (auto const& assn: *handle) {
320  // assn is a std::pair<art::Ptr<Source_t>, art::Ptr<Dest_t>>
321  art::Ptr<Source_t> const& src = assn.first;
322 
323  if (src.isNull()) {
324  MF_LOG_ERROR("FindAllP") << "Empty pointer found in association "
325  << handle.provenance();
326  continue; // this should not happen
327  }
328 
329  art::Ptr<Dest_t> const& dest = assn.second;
330 
331  // if we have changed product (that should be fairly rare),
332  // update the running pointers
333  if (src.id() != LastProductID) {
334  LastProductID = src.id();
335  AssnsList = &(cache.AssnCache[LastProductID]);
336 
337  // if the list is empty, it means we have just created it!
338  if (AssnsList->empty()) {
339  // allocate enough space to accomodate all the associations,
340  // (provided that source IDs are sequencial);
341  // in fact typically all the associations in the same handle
342  // have the same product ID
343  ResizeToPower2(*AssnsList, handle->size());
344  }
345  } // if different product ID
346 
347  // make sure there is enough room in the vector
348  typename art::Ptr<Source_t>::key_type key = src.key();
349  if (key >= AssnsList->size()) ResizeToPower2(*AssnsList, key + 1);
350 
351  // store the association to dest
352  art::Ptr<Dest_t>& dest_cell = (*AssnsList)[key];
353  if (dest_cell.isNonnull() && (dest_cell != dest)) {
354  throw art::Exception(art::errors::InvalidNumber)
355  << "Object Ptr" << src
356  << " is associated with at least two objects: "
357  << dest << " and " << dest_cell;
358  }
359  dest_cell = dest;
360  ++count;
361  } // for all associations in a list
362 
363  MF_LOG_DEBUG("FindAllP")
364  << "Merged " << count << " associations from " << handle.provenance();
365  return count;
366  } // FindAllP::Merge()
367 
368 
369 
370  //------------------------------------------------------------------------
371  template <>
373  (argument_type const& id) const -> result_type
374  {
375  // make sure we have enough bits in result_type;
376  // if not, we need a more clever algorithm
377  //static_assert(
378  // sizeof(id.processIndex()) + sizeof(id.productIndex())
379  // <= sizeof(result_type),
380  // "hash return type not large enough for hashing art::ProductID"
381  //);
382  // stack the process and product IDs in one integer
383  //return result_type(
384  //(id.processIndex() << sizeof(id.productIndex() * CHAR_BIT))
385  //+ id.productIndex()
386  //);
387  return result_type( id.value() );
388  } // hash<art::ProductID>::operator()
389 
390 
391 
392  //------------------------------------------------------------------------
393  template <typename T>
394  void ResizeToPower2(std::vector<T>& v, size_t min_size) {
395  if (min_size == 0) {
396  v.clear();
397  return;
398  }
399  size_t new_size = 1;
400  while (new_size < min_size) new_size *= 2;
401 
402  v.resize(new_size);
403  } // ResizeToPower2()
404 
405  } // namespace details
406  } // namespace util
407 } // namespace lar
408 
409 
410 //------------------------------------------------------------------------------
411 #endif // FINDALLP_H 1
FindAllP(art::Event &event, art::InputTag assnTag)
Constructor: reads one association from the specified event.
Definition: FindAllP.h:129
unsigned int Merge(art::Handle< Assns_t > &handle)
Adds all associations in the specified handle; returns their number.
Definition: FindAllP.h:308
bool hasProduct(art::ProductID const &id) const
Returns whether there are associations from objects in product id.
Definition: FindAllP.h:244
A class holding many associations between objects.
Definition: FindAllP.h:65
DestPtr_t operator[](SourcePtr_t const &src) const
Returns the specified element of the cache.
Definition: FindAllP.h:94
void clear()
Empties the cache.
Definition: FindAllP.h:98
result_type operator()(argument_type const &v) const
Cache_t AssnCache
association cache, keyed by product ID and index
Definition: FindAllP.h:83
FindAllP()=default
Default constructor: empty query, read information with Read()
Cache_t cache
set of associations, keyed by product ID and key
Definition: FindAllP.h:190
FindAllP(art::Event &event)
Constructor: reads all associations from the specified event.
Definition: FindAllP.h:126
std::vector< DestPtr_t > InProductCache_t
type for a cache of dest products for a given source product ID
Definition: FindAllP.h:74
unsigned int Add(art::Event &event, art::InputTag const &assnTag)
Reads the specified association from the event.
Definition: FindAllP.h:292
unsigned int Read(art::Event &event)
Reads all the associations from the event.
Definition: FindAllP.h:256
Query object reading all the associations between two classes.
Definition: FindAllP.h:117
void ResizeToPower2(std::vector< T > &v, size_t min_size)
Resizes a vector to a size power of 2, with a minimum size.
Definition: FindAllP.h:394
art::Assns< Source_t, Dest_t > Assns_t
Definition: FindAllP.h:185
Hash functions for art and larsoft objects.
Definition: FindAllP.h:42
std::unordered_map< art::ProductID, InProductCache_t, Hash_t > Cache_t
type for the complete cache, keyed by source product ID
Definition: FindAllP.h:81
temporary value
std::size_t count(Cont const &cont)
art::Ptr< Dest_t > const & operator[](art::Ptr< Dest_t > const &src) const
Returns the object associated to the specified one.
Definition: FindAllP.h:230
UniqueAssociationCache()=default
Constructor: an empty cache.