IsStdOptional

IsStdOptional

ThinningProducer

Macros

Line Code
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
#ifndef FWCore_Framework_ThinningProducer_h
#define FWCore_Framework_ThinningProducer_h

/** \class edm::ThinningProducer
\author W. David Dagenhart, created 11 June 2014
*/

#include "FWCore/Framework/interface/stream/EDProducer.h"

#include "DataFormats/Common/interface/Handle.h"
#include "DataFormats/Common/interface/OrphanHandle.h"
#include "DataFormats/Common/interface/ThinnedAssociation.h"
#include "DataFormats/Common/interface/fillCollectionForThinning.h"
#include "DataFormats/Provenance/interface/ProductRegistry.h"
#include "DataFormats/Provenance/interface/ThinnedAssociationsHelper.h"
#include "FWCore/Framework/interface/Event.h"
#include "FWCore/ParameterSet/interface/ParameterSet.h"
#include "FWCore/Utilities/interface/EDGetToken.h"
#include "FWCore/Utilities/interface/InputTag.h"
#include "FWCore/Utilities/interface/propagate_const.h"
#include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
#include "FWCore/ParameterSet/interface/ParameterSetDescription.h"

#include <memory>
#include <optional>
#include <type_traits>

namespace edm {

  class EventSetup;

  namespace detail {
    template <typename T>
    struct IsStdOptional {
      static constexpr bool value = false;
    };
    template <typename T>
    struct IsStdOptional<std::optional<T>> {
      static constexpr bool value = true;
    };

    template <typename Item, typename Selector, typename Collection>
    void fillCollectionForThinning(Item const& item,
                                   Selector& selector,
                                   unsigned int iIndex,
                                   Collection& output,
                                   ThinnedAssociation& association) {
      using SelectorChooseReturnType = decltype(selector.choose(0U, std::declval<Item const&>()));
      constexpr bool isSlimming = detail::IsStdOptional<SelectorChooseReturnType>::value;
      if constexpr (isSlimming) {
        std::optional<typename SelectorChooseReturnType::value_type> obj = selector.choose(iIndex, item);
        if (obj.has_value()) {
          // move to support std::unique_ptr<T> with edm::OwnVector<T> or std::vector<unique_ptr<T>>
          output.push_back(std::move(*obj));
          association.push_back(iIndex);
        }
      } else {
        if (selector.choose(iIndex, item)) {
          output.push_back(item);
          association.push_back(iIndex);
        }
      }
    }

  }  // namespace detail

  template <typename Collection, typename Selector>
  class ThinningProducer : public stream::EDProducer<> {
  public:
    explicit ThinningProducer(ParameterSet const& pset);
    ~ThinningProducer() override;

    static void fillDescriptions(ConfigurationDescriptions& descriptions);

    void produce(Event& event, EventSetup const& eventSetup) override;

    void registerThinnedAssociations(ProductRegistry const& productRegistry,
                                     ThinnedAssociationsHelper& thinnedAssociationsHelper) override;

  private:
    edm::propagate_const<std::unique_ptr<Selector>> selector_;
    edm::EDGetTokenT<Collection> inputToken_;
    edm::InputTag inputTag_;
    edm::EDPutTokenT<Collection> outputToken_;
    edm::EDPutTokenT<ThinnedAssociation> thinnedOutToken_;

    using SelectorChooseReturnType =
        decltype(selector_->choose(0U, std::declval<typename detail::ElementType<Collection>::type const>()));
    static constexpr bool isSlimming = detail::IsStdOptional<SelectorChooseReturnType>::value;
    static_assert(
        std::is_same_v<SelectorChooseReturnType, bool> || isSlimming,
        "Selector::choose() must return bool (for pure thinning) or std::optional<ElementType> (for slimming)");
  };

  template <typename Collection, typename Selector>
  ThinningProducer<Collection, Selector>::ThinningProducer(ParameterSet const& pset)
      : selector_(new Selector(pset, consumesCollector())) {
    inputTag_ = pset.getParameter<InputTag>("inputTag");
    inputToken_ = consumes<Collection>(inputTag_);

    outputToken_ = produces<Collection>();
    thinnedOutToken_ = produces<ThinnedAssociation>();
  }

  template <typename Collection, typename Selector>
  ThinningProducer<Collection, Selector>::~ThinningProducer() {}

  template <typename Collection, typename Selector>
  void ThinningProducer<Collection, Selector>::fillDescriptions(ConfigurationDescriptions& descriptions) {
    ParameterSetDescription desc;
    desc.setComment("Produces thinned collections and associations to them");
    desc.add<edm::InputTag>("inputTag");
    Selector::fillPSetDescription(desc);
    descriptions.addWithDefaultLabel(desc);
  }

  template <typename Collection, typename Selector>
  void ThinningProducer<Collection, Selector>::produce(Event& event, EventSetup const& eventSetup) {
    auto inputCollection = event.getHandle(inputToken_);

    edm::Event const& constEvent = event;
    selector_->preChoose(inputCollection, constEvent, eventSetup);

    Collection thinnedCollection;
    ThinnedAssociation thinnedAssociation;

    unsigned int iIndex = 0;
    for (auto iter = inputCollection->begin(), iterEnd = inputCollection->end(); iter != iterEnd; ++iter, ++iIndex) {
      using namespace detail;
      fillCollectionForThinning(*iter, *selector_, iIndex, thinnedCollection, thinnedAssociation);
    }
    selector_->reset();

    OrphanHandle<Collection> orphanHandle = event.emplace(outputToken_, std::move(thinnedCollection));

    thinnedAssociation.setParentCollectionID(inputCollection.id());
    thinnedAssociation.setThinnedCollectionID(orphanHandle.id());
    event.emplace(thinnedOutToken_, std::move(thinnedAssociation));
  }

  template <typename Collection, typename Selector>
  void ThinningProducer<Collection, Selector>::registerThinnedAssociations(
      ProductRegistry const& productRegistry, ThinnedAssociationsHelper& thinnedAssociationsHelper) {
    BranchID associationID;
    BranchID thinnedCollectionID;

    // If the InputTag does not specify the process name, it is
    // possible that there will be more than one match found below.
    // For a particular event only one match is correct and the
    // others will be false. It even possible for some events one
    // match is correct and for others another is correct. This is
    // a side effect of the lookup mechanisms when the process name
    // is not specified.
    // When using the registry this generates one would have to
    // check the ProductIDs in ThinnedAssociation product to get
    // the correct association. This ambiguity will probably be
    // rare and possibly never occur in practice.
    std::vector<BranchID> parentCollectionIDs;

    ProductRegistry::ProductList const& productList = productRegistry.productList();
    for (auto const& product : productList) {
      ProductDescription const& desc = product.second;
      if (desc.dropped()) {
        // Dropped branch does not have type information, but they can
        // be ignored here because all of the parent/thinned/association
        // branches are expected to be present
        continue;
      }
      if (desc.unwrappedType().typeInfo() == typeid(Collection)) {
        if (desc.produced() && desc.moduleLabel() == moduleDescription().moduleLabel() &&
            desc.productInstanceName().empty()) {
          thinnedCollectionID = desc.branchID();
        }
        if (desc.moduleLabel() == inputTag_.label() && desc.productInstanceName() == inputTag_.instance()) {
          if (inputTag_.willSkipCurrentProcess()) {
            if (!desc.produced()) {
              parentCollectionIDs.push_back(desc.branchID());
            }
          } else if (inputTag_.process().empty() || inputTag_.process() == desc.processName()) {
            if (desc.produced()) {
              parentCollectionIDs.push_back(desc.originalBranchID());
            } else {
              parentCollectionIDs.push_back(desc.branchID());
            }
          }
        }
      }
      if (desc.produced() && desc.unwrappedType().typeInfo() == typeid(ThinnedAssociation) &&
          desc.moduleLabel() == moduleDescription().moduleLabel() && desc.productInstanceName().empty()) {
        associationID = desc.branchID();
      }
    }
    if (parentCollectionIDs.empty()) {
      // This could happen if the input collection was dropped. Go ahead and add
      // an entry and let the exception be thrown only if the module is run (when
      // it cannot find the product).
      thinnedAssociationsHelper.addAssociation(BranchID(), associationID, thinnedCollectionID, isSlimming);
    } else {
      for (auto const& parentCollectionID : parentCollectionIDs) {
        thinnedAssociationsHelper.addAssociation(parentCollectionID, associationID, thinnedCollectionID, isSlimming);
      }
    }
  }
}  // namespace edm
#endif