1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
|
#ifndef DataFormats_Common_getThinned_implementation_h
#define DataFormats_Common_getThinned_implementation_h
#include <algorithm>
#include <cassert>
#include <functional>
#include <optional>
#include <tuple>
#include <variant>
#include <limits>
#include "DataFormats/Common/interface/ThinnedAssociation.h"
#include "DataFormats/Provenance/interface/BranchID.h"
#include "DataFormats/Provenance/interface/ProductID.h"
#include "DataFormats/Provenance/interface/ThinnedAssociationsHelper.h"
#include "FWCore/Utilities/interface/EDMException.h"
namespace edm {
class WrapperBase;
namespace detail {
constexpr unsigned int kThinningDoNotLookForThisIndex = std::numeric_limits<unsigned int>::max();
class ThinnedOrSlimmedProduct {
public:
ThinnedOrSlimmedProduct() = default;
explicit ThinnedOrSlimmedProduct(WrapperBase const* thinned, unsigned int key)
: thinnedProduct_{thinned}, thinnedKey_{key} {}
explicit ThinnedOrSlimmedProduct(ThinnedAssociation const* slimmed, unsigned int key)
: slimmedAssociation_{slimmed}, thinnedKey_{key} {}
bool hasThinned() const { return thinnedProduct_ != nullptr; }
bool hasSlimmed() const { return slimmedAssociation_ != nullptr; }
std::tuple<WrapperBase const*, unsigned int> thinnedProduct() const {
return std::tuple(thinnedProduct_, thinnedKey_);
}
std::tuple<ThinnedAssociation const*, unsigned int> slimmedAssociation() const {
return std::tuple(slimmedAssociation_, thinnedKey_);
}
private:
WrapperBase const* thinnedProduct_ = nullptr;
ThinnedAssociation const* slimmedAssociation_ = nullptr;
unsigned int thinnedKey_ = 0;
};
// This is a helper function to recursively search for a thinned
// product containing the parent key on the same "slimming depth". That
// means that when the recursion encounters a slimmed collection,
// the tree traversal does not proceed onto the children of the
// slimmed collection. Instead, the slimmed ThinnedAssociation is
// recorded for the case that the entire tree on a given "slimming
// depth" does not have any thinned-only collections.
//
// Returns
// - (WrapperBase, unsigned) in case a thinned collection
// containing the parent key was found.
// - (ThinnedAssociation, unsigned) in case no thinned collections
// were encountered, but a slimmed collection containing the
// parent key was found
// - otherwise "null" (i.e. only thinned collections without the
// parent key, or in absence of thinned collections the slimmed
// collection without the parent key, or no thinned or slimmed
// collections)
template <typename F1, typename F2, typename F3>
ThinnedOrSlimmedProduct getThinnedProductOnSlimmingDepth(ProductID const& pid,
unsigned int key,
ThinnedAssociationsHelper const& thinnedAssociationsHelper,
F1 pidToBid,
F2 getThinnedAssociation,
F3 getByProductID) {
BranchID parent = pidToBid(pid);
auto associatedBranches = thinnedAssociationsHelper.parentBegin(parent);
auto const iEnd = thinnedAssociationsHelper.parentEnd(parent);
if (associatedBranches == iEnd) {
return ThinnedOrSlimmedProduct();
}
bool const slimmedAllowed = (associatedBranches + 1 == iEnd);
if (slimmedAllowed and associatedBranches->isSlimmed()) {
// Slimmed container can be considered only if it has no (thinned) siblings
ThinnedAssociation const* slimmedAssociation = getThinnedAssociation(associatedBranches->association());
if (slimmedAssociation == nullptr or
associatedBranches->parent() != pidToBid(slimmedAssociation->parentCollectionID())) {
return ThinnedOrSlimmedProduct();
}
// Does this slimmed container have the element referenced by key?
auto slimmedIndex = slimmedAssociation->getThinnedIndex(key);
if (slimmedIndex.has_value()) {
return ThinnedOrSlimmedProduct(slimmedAssociation, *slimmedIndex);
} else {
return ThinnedOrSlimmedProduct();
}
}
// Loop over thinned containers which were made by selecting elements from the parent container
for (; associatedBranches != iEnd; ++associatedBranches) {
if (associatedBranches->isSlimmed()) {
continue;
}
ThinnedAssociation const* thinnedAssociation = getThinnedAssociation(associatedBranches->association());
if (thinnedAssociation == nullptr)
continue;
if (associatedBranches->parent() != pidToBid(thinnedAssociation->parentCollectionID())) {
continue;
}
// Does this thinned container have the element referenced by key?
auto thinnedIndex = thinnedAssociation->getThinnedIndex(key);
if (not thinnedIndex.has_value()) {
continue;
}
// Return a pointer to thinned container if we can find it
ProductID const& thinnedCollectionPID = thinnedAssociation->thinnedCollectionID();
WrapperBase const* thinnedCollection = getByProductID(thinnedCollectionPID);
if (thinnedCollection != nullptr) {
return ThinnedOrSlimmedProduct(thinnedCollection, *thinnedIndex);
}
// Thinned container is not found, try looking recursively in thinned containers
// which were made by selecting elements from this thinned container.
auto thinnedOrSlimmed = getThinnedProductOnSlimmingDepth(thinnedCollectionPID,
*thinnedIndex,
thinnedAssociationsHelper,
pidToBid,
getThinnedAssociation,
getByProductID);
if (thinnedOrSlimmed.hasThinned() or (slimmedAllowed and thinnedOrSlimmed.hasSlimmed())) {
return thinnedOrSlimmed;
}
}
return ThinnedOrSlimmedProduct();
}
inline auto makeThinnedIndexes(std::vector<unsigned int> const& keys,
std::vector<WrapperBase const*> const& foundContainers,
ThinnedAssociation const* thinnedAssociation) {
unsigned const nKeys = keys.size();
std::vector<unsigned int> thinnedIndexes(nKeys, kThinningDoNotLookForThisIndex);
bool hasAny = false;
for (unsigned k = 0; k < nKeys; ++k) {
// Already found this one
if (foundContainers[k] != nullptr) {
continue;
}
// Already know this one is not in this thinned container
if (keys[k] == kThinningDoNotLookForThisIndex) {
continue;
}
// Does the thinned container hold the entry of interest?
if (auto thinnedIndex = thinnedAssociation->getThinnedIndex(keys[k]); thinnedIndex.has_value()) {
thinnedIndexes[k] = *thinnedIndex;
hasAny = true;
}
}
return std::tuple(std::move(thinnedIndexes), hasAny);
}
// This is a helper function to recursive search ffor thinned
// collections that contain some of the parent keys on the same
// "slimming depth". That means that when the recursion encounters
// a slimmed colleciton, the tree traversal does not proceed onto
// the children of the slimmed collection. Instead, the slimmed
// ThinnedAssociation is recorded for the case that the entire
// tree on a given "slimming depth" does not have any thinned-only
// collections.
//
// Returns a (ThinnedAssociation, vector<unsigned>) in case no
// thinned collections were encountered, but a slimmed collection
// containing at least one of the parent keys was found. The
// returned vector contains keys to the slimmed collection, and
// the output arguments foundContainers and keys are not modified
// in this case.
//
// Otherwise returns a null optional (i.e. any thinned collection
// was encountered, or in absence of thinned collections the
// slimmed collection did not contain any parent keys, or there
// were no thinned or slimmed collections)
template <typename F1, typename F2, typename F3>
std::optional<std::tuple<ThinnedAssociation const*, std::vector<unsigned int>>> getThinnedProductsOnSlimmingDepth(
ProductID const& pid,
ThinnedAssociationsHelper const& thinnedAssociationsHelper,
F1 pidToBid,
F2 getThinnedAssociation,
F3 getByProductID,
std::vector<WrapperBase const*>& foundContainers,
std::vector<unsigned int>& keys) {
BranchID parent = pidToBid(pid);
auto associatedBranches = thinnedAssociationsHelper.parentBegin(parent);
auto const iEnd = thinnedAssociationsHelper.parentEnd(parent);
if (associatedBranches == iEnd) {
return std::nullopt;
}
bool const slimmedAllowed = associatedBranches + 1 == iEnd;
if (slimmedAllowed and associatedBranches->isSlimmed()) {
// Slimmed container can be considered only if it has no (thinned) siblings
ThinnedAssociation const* slimmedAssociation = getThinnedAssociation(associatedBranches->association());
if (slimmedAssociation == nullptr or
associatedBranches->parent() != pidToBid(slimmedAssociation->parentCollectionID())) {
return std::nullopt;
}
auto [slimmedIndexes, hasAny] = makeThinnedIndexes(keys, foundContainers, slimmedAssociation);
// Does this slimmed container have any of the elements referenced by keys?
if (hasAny) {
return std::tuple(slimmedAssociation, std::move(slimmedIndexes));
} else {
return std::nullopt;
}
}
// Loop over thinned containers which were made by selecting elements from the parent container
for (; associatedBranches != iEnd; ++associatedBranches) {
if (associatedBranches->isSlimmed()) {
continue;
}
ThinnedAssociation const* thinnedAssociation = getThinnedAssociation(associatedBranches->association());
if (thinnedAssociation == nullptr)
continue;
if (associatedBranches->parent() != pidToBid(thinnedAssociation->parentCollectionID())) {
continue;
}
auto [thinnedIndexes, hasAny] = makeThinnedIndexes(keys, foundContainers, thinnedAssociation);
if (!hasAny) {
continue;
}
// Set the pointers and indexes into the thinned container (if we can find it)
ProductID thinnedCollectionPID = thinnedAssociation->thinnedCollectionID();
WrapperBase const* thinnedCollection = getByProductID(thinnedCollectionPID);
unsigned const nKeys = keys.size();
if (thinnedCollection == nullptr) {
// Thinned container is not found, try looking recursively in thinned containers
// which were made by selecting elements from this thinned container.
auto slimmed = getThinnedProductsOnSlimmingDepth(thinnedCollectionPID,
thinnedAssociationsHelper,
pidToBid,
getThinnedAssociation,
getByProductID,
foundContainers,
thinnedIndexes);
if (slimmedAllowed and slimmed.has_value()) {
return slimmed;
}
for (unsigned k = 0; k < nKeys; ++k) {
if (foundContainers[k] == nullptr)
continue;
if (thinnedIndexes[k] == kThinningDoNotLookForThisIndex)
continue;
keys[k] = thinnedIndexes[k];
}
} else {
for (unsigned k = 0; k < nKeys; ++k) {
if (thinnedIndexes[k] == kThinningDoNotLookForThisIndex)
continue;
keys[k] = thinnedIndexes[k];
foundContainers[k] = thinnedCollection;
}
}
}
return std::nullopt;
}
// This function provides a common implementation of
// EDProductGetter::getThinnedProduct() for EventPrincipal,
// DataGetterHelper, and BareRootProductGetter.
//
// getThinnedProduct assumes getIt was already called and failed to find
// the product. The input key is the index of the desired element in the
// container identified by ProductID (which cannot be found).
// If the return value is not null, then the desired element was
// found in a thinned container. If the desired element is not
// found, then an optional without a value is returned.
template <typename F1, typename F2, typename F3>
std::optional<std::tuple<WrapperBase const*, unsigned int>> getThinnedProduct(
ProductID const& pid,
unsigned int key,
ThinnedAssociationsHelper const& thinnedAssociationsHelper,
F1 pidToBid,
F2 getThinnedAssociation,
F3 getByProductID) {
auto thinnedOrSlimmed = getThinnedProductOnSlimmingDepth(
pid, key, thinnedAssociationsHelper, pidToBid, getThinnedAssociation, getByProductID);
if (thinnedOrSlimmed.hasThinned()) {
return thinnedOrSlimmed.thinnedProduct();
} else if (thinnedOrSlimmed.hasSlimmed()) {
auto [slimmedAssociation, slimmedIndex] = thinnedOrSlimmed.slimmedAssociation();
ProductID const& slimmedCollectionPID = slimmedAssociation->thinnedCollectionID();
WrapperBase const* slimmedCollection = getByProductID(slimmedCollectionPID);
if (slimmedCollection == nullptr) {
// Slimmed container is not found, try looking recursively in thinned containers
// which were made by selecting elements from this thinned container.
return getThinnedProduct(slimmedCollectionPID,
slimmedIndex,
thinnedAssociationsHelper,
pidToBid,
getThinnedAssociation,
getByProductID);
}
return std::tuple(slimmedCollection, slimmedIndex);
}
return std::nullopt;
}
// This function provides a common implementation of
// EDProductGetter::getThinnedProducts() for EventPrincipal,
// DataGetterHelper, and BareRootProductGetter.
//
// getThinnedProducts assumes getIt was already called and failed to find
// the product. The input keys are the indexes into the container identified
// by ProductID (which cannot be found). On input the WrapperBase pointers
// must all be set to nullptr (except when the function calls itself
// recursively where non-null pointers mark already found elements).
// Thinned containers derived from the product are searched to see
// if they contain the desired elements. For each that is
// found, the corresponding WrapperBase pointer is set and the key
// is modified to be the key into the container where the element
// was found. The WrapperBase pointers might or might not all point
// to the same thinned container.
template <typename F1, typename F2, typename F3>
void getThinnedProducts(ProductID const& pid,
ThinnedAssociationsHelper const& thinnedAssociationsHelper,
F1 pidToBid,
F2 getThinnedAssociation,
F3 getByProductID,
std::vector<WrapperBase const*>& foundContainers,
std::vector<unsigned int>& keys) {
auto slimmed = getThinnedProductsOnSlimmingDepth(
pid, thinnedAssociationsHelper, pidToBid, getThinnedAssociation, getByProductID, foundContainers, keys);
if (slimmed.has_value()) {
// no thinned procucts found, try out slimmed next if one is available
auto [slimmedAssociation, slimmedIndexes] = std::move(*slimmed);
ProductID const& slimmedCollectionPID = slimmedAssociation->thinnedCollectionID();
WrapperBase const* slimmedCollection = getByProductID(slimmedCollectionPID);
unsigned const nKeys = keys.size();
if (slimmedCollection == nullptr) {
getThinnedProducts(slimmedCollectionPID,
thinnedAssociationsHelper,
pidToBid,
getThinnedAssociation,
getByProductID,
foundContainers,
slimmedIndexes);
for (unsigned k = 0; k < nKeys; ++k) {
if (foundContainers[k] == nullptr)
continue;
if (slimmedIndexes[k] == kThinningDoNotLookForThisIndex)
continue;
keys[k] = slimmedIndexes[k];
}
} else {
for (unsigned k = 0; k < nKeys; ++k) {
if (slimmedIndexes[k] == kThinningDoNotLookForThisIndex)
continue;
keys[k] = slimmedIndexes[k];
foundContainers[k] = slimmedCollection;
}
}
}
}
using GetThinnedKeyFromExceptionFactory = std::function<edm::Exception()>;
// This function provides a common implementation of
// EDProductGetter::getThinnedKeyFrom() for EventPrincipal,
// DataGetterHelper, and BareRootProductGetter.
//
// The thinned ProductID must come from an existing RefCore. The
// input key is the index of the desired element in the container
// identified by the parent ProductID. Returns an std::variant
// whose contents can be
// - unsigned int for the index in the thinned collection if the
// desired element was found in the thinned collection
// - function creating an edm::Exception if parent is not a parent
// of any thinned collection, thinned is not really a thinned
// collection, or parent and thinned have no thinning
// relationship
// - std::monostate if thinned is thinned from parent, but the key
// is not found in the thinned collection
template <typename F>
std::variant<unsigned int, GetThinnedKeyFromExceptionFactory, std::monostate> getThinnedKeyFrom_implementation(
ProductID const& parentID,
BranchID const& parent,
unsigned int key,
ProductID const& thinnedID,
BranchID thinned,
ThinnedAssociationsHelper const& thinnedAssociationsHelper,
F&& getThinnedAssociation) {
// need to explicitly check for equality of parent BranchID,
// because ThinnedAssociationsHelper::parentBegin() uses
// std::lower_bound() that returns a valid iterator in case the
// parent is not found
if (auto iParent = thinnedAssociationsHelper.parentBegin(parent);
iParent == thinnedAssociationsHelper.parentEnd(parent) or iParent->parent() != parent) {
return [parentID]() {
return Exception(errors::InvalidReference)
<< "Parent collection with ProductID " << parentID << " has not been thinned";
};
}
bool foundParent = false;
std::vector<ThinnedAssociation const*> thinnedAssociationParentage;
while (not foundParent) {
// TODO: be smarter than linear search every time?
auto branchesToThinned = std::find_if(
thinnedAssociationsHelper.begin(), thinnedAssociationsHelper.end(), [&thinned](auto& associatedBranches) {
return associatedBranches.thinned() == thinned;
});
if (branchesToThinned == thinnedAssociationsHelper.end()) {
return [parentID, thinnedID, thinnedIsThinned = not thinnedAssociationParentage.empty()]() {
Exception ex(errors::InvalidReference);
ex << "Requested thinned collection with ProductID " << thinnedID
<< " is not thinned from the parent collection with ProductID " << parentID
<< " or from any collection thinned from it.";
if (not thinnedIsThinned) {
ex << " In fact, the collection " << thinnedID
<< " passed in as a 'thinned' collection has not been thinned at all.";
}
return ex;
};
}
ThinnedAssociation const* thinnedAssociation = getThinnedAssociation(branchesToThinned->association());
if (thinnedAssociation == nullptr) {
Exception ex{errors::LogicError};
if (thinnedAssociationParentage.empty()) {
ex << "ThinnedAssociation corresponding to thinned collection with ProductID " << thinnedID
<< " not found.";
} else {
ex << "Intermediate ThinnedAssociation between the requested thinned ProductID " << thinnedID
<< " and parent " << parentID << " not found.";
}
ex << " This should not happen.\nPlease contact the core framework developers.";
throw ex;
}
thinnedAssociationParentage.push_back(thinnedAssociation);
if (branchesToThinned->parent() == parent) {
foundParent = true;
} else {
// next iteration with current parent as the thinned collection
thinned = branchesToThinned->parent();
}
}
// found the parent, now need to rewind the parentage chain to
// find the index in the requested thinned collection
unsigned int thinnedIndex = key;
for (auto iAssociation = thinnedAssociationParentage.rbegin(), iEnd = thinnedAssociationParentage.rend();
iAssociation != iEnd;
++iAssociation) {
auto optIndex = (*iAssociation)->getThinnedIndex(thinnedIndex);
if (optIndex) {
thinnedIndex = *optIndex;
} else {
return std::monostate{};
}
}
return thinnedIndex;
}
} // namespace detail
} // namespace edm
#endif
|