|
8 | 8 | // granted to it by virtue of its status as an Intergovernmental Organization |
9 | 9 | // or submit itself to any jurisdiction. |
10 | 10 | #include "Framework/runDataProcessing.h" |
11 | | -#include "Framework/RCombinedDS.h" |
12 | | -#include "Framework/TableBuilder.h" |
| 11 | +#include "Framework/AnalysisHelpers.h" |
13 | 12 |
|
14 | 13 | #include <ROOT/RDataFrame.hxx> |
15 | | -#include <ROOT/RArrowDS.hxx> |
16 | 14 |
|
| 15 | +using namespace ROOT::RDF; |
17 | 16 | using namespace o2::framework; |
18 | 17 |
|
19 | 18 | // A dummy workflow which creates a few of the tables proposed by Ruben, |
20 | 19 | // using ARROW |
21 | 20 | WorkflowSpec defineDataProcessing(ConfigContext const& specs) |
22 | 21 | { |
| 22 | + // Workflow definition. A workflow can be one or more DataProcessors |
| 23 | + // each implementing (part of) an analysis. Each DataProcessor has |
| 24 | + // (at least) a name, some Inputs, some Outputs and they get arranged |
| 25 | + // together accordingly. |
23 | 26 | WorkflowSpec workflow{ |
24 | | - /// Minimal analysis example |
| 27 | + // Multiple DataProcessor specs |
| 28 | + // can be provided per workflow |
25 | 29 | DataProcessorSpec{ |
| 30 | + // The name of my analysis |
26 | 31 | "d0-analysis", |
27 | | - { |
| 32 | + Inputs{ |
28 | 33 | // Dangling inputs of type AOD will be automatically picked up |
29 | 34 | // by DPL and an extra reader device will be instanciated to |
30 | | - // read them. |
31 | | - InputSpec{ "DZeroFlagged", "AOD", "DZEROFLAGGED" }, |
| 35 | + // read them. In this particular case the signature |
| 36 | + // AOD/DZEROFLAGGED is associated to Gianmichele's |
| 37 | + // D0 candidates schema. The first string is just a label |
| 38 | + // so that the algorithm can be in principle be reused for different |
| 39 | + // kind of candidates. |
| 40 | + InputSpec{ "candidates", "AOD", "DZEROFLAGGED" }, |
32 | 41 | }, |
33 | | - {}, |
| 42 | + // No outputs for the time being. |
| 43 | + Outputs{}, |
34 | 44 | AlgorithmSpec{ |
35 | | - [](InitContext& setup) { |
36 | | - return [](ProcessingContext& ctx) { |
37 | | - auto s = ctx.inputs().get<TableConsumer>("DZeroFlagged"); |
38 | | - /// From the handle, we construct the actual arrow table |
39 | | - /// which is then used as a source for the RDataFrame. |
40 | | - /// This is probably easy to change to a: |
41 | | - /// |
42 | | - /// auto rdf = ctx.inputs().get<RDataSource>("xz"); |
43 | | - auto table = s->asArrowTable(); |
44 | | - using namespace ROOT::RDF; |
| 45 | + // This is the actual per "message" loop, where a message could |
| 46 | + // be the contents of a file or part of it. |
| 47 | + // FIXME: Too much boilerplate. |
| 48 | + adaptStateless([](InputRecord& inputs) { |
| 49 | + auto input = inputs.get<TableConsumer>("candidates"); |
45 | 50 |
|
46 | | - TFile f("result.root", "RECREATE"); |
| 51 | + // This does a single loop on all the candidates in the input message |
| 52 | + // using a simple mask on the cand_type_ML column and does |
| 53 | + // a simple 1D histogram of the filtered entries. |
| 54 | + auto candidates = o2::analysis::doSingleLoopOn(input); |
47 | 55 |
|
48 | | - auto flatD0 = std::make_unique<RArrowDS>(table, std::vector<std::string>{}); |
49 | | - ROOT::RDataFrame rdf1(std::move(flatD0)); |
50 | | - /// A single loop to do an invariant mass plot, where we use the preselected |
51 | | - /// candidates |
52 | | - auto candFilter = [](int x) -> bool { return x & 0x1; }; |
53 | | - auto h1 = rdf1.Filter(candFilter, { "cand_type_ML" }).Histo1D("inv_mass_ML"); |
54 | | - h1->SetName("InvariantMass"); |
55 | | - h1->Write(); |
| 56 | + auto h1 = candidates.Filter("(bool)(cand_type_ML & 0x1)").Histo1D("inv_mass_ML"); |
56 | 57 |
|
57 | | - /// Double loops on all supported loop types. |
58 | | - using Index = RCombinedDSBlockJoinIndex<int>; |
59 | | - auto types = { |
60 | | - BlockCombinationRule::Anti, |
61 | | - BlockCombinationRule::Full, |
62 | | - BlockCombinationRule::Diagonal, |
63 | | - BlockCombinationRule::StrictlyUpper, |
64 | | - BlockCombinationRule::Upper, |
65 | | - }; |
66 | | - // A few helpers |
67 | | - auto bothCandFilter = [](int x, int y) -> bool { return x & 0x1 && y & 0x1; }; |
68 | | - auto delta = [](float x, float y) { return x - y; }; |
| 58 | + // A lambda function subtracting two quantities. This defines |
| 59 | + // a function "delta" which can be invoked with |
| 60 | + // |
| 61 | + // delta(1,2) |
| 62 | + // |
| 63 | + // and will return 1 - 2. |
| 64 | + auto delta = [](float x, float y) { return x - y; }; |
69 | 65 |
|
70 | | - for (auto combinationType : types) { |
71 | | - auto d0 = std::make_unique<RArrowDS>(table, std::vector<std::string>{}); |
72 | | - auto d0bar = std::make_unique<RArrowDS>(table, std::vector<std::string>{}); |
73 | | - auto d0d0bar = std::make_unique<RCombinedDS>(std::move(d0), std::move(d0bar), std::move(std::make_unique<Index>("cand_evtID_ML", true, combinationType)), "d0_", "d0bar_"); |
| 66 | + // This does all the combinations for all the candidates which have |
| 67 | + // the same value for cand_evtID_ML (the Event ID). |
| 68 | + // d0_ is the prefix assigned to the outer variable of the double loop. |
| 69 | + // d0bar_ is the prefix assigned to the inner variable of the double loop. |
| 70 | + // |
| 71 | + // The lines below will: |
| 72 | + // * Filter the combinations according to some mask |
| 73 | + // * Define a column delta_phi with the difference in phi between d0 and d0bar phi |
| 74 | + // * Define a column delta_eta with the difference in phi between d0 and d0bar eta |
| 75 | + // * Do two histograms with delta_phi, delta_eta |
| 76 | + auto combinations = o2::analysis::doSelfCombinationsWith(input, "d0", "cand_evtID_ML"); |
| 77 | + auto deltas = combinations.Filter("d0_cand_type_ML & 0x1 && d0bar_cand_type_ML & 0x1") |
| 78 | + .Define("delta_phi", delta, { "d0_phi_cand_ML", "d0bar_phi_cand_ML" }) |
| 79 | + .Define("delta_eta", delta, { "d0_eta_cand_ML", "d0bar_eta_cand_ML" }); |
| 80 | + auto h2 = deltas.Histo1D("delta_phi"); |
| 81 | + auto h3 = deltas.Histo1D("delta_eta"); |
74 | 82 |
|
75 | | - ROOT::RDataFrame rdf2(std::move(d0d0bar)); |
76 | | - auto combinatorics = rdf2.Filter(bothCandFilter, { "d0_cand_type_ML", "d0bar_cand_type_ML" }) |
77 | | - .Define("delta_phi", delta, { "d0_phi_cand_ML", "d0bar_phi_cand_ML" }) |
78 | | - .Define("delta_eta", delta, { "d0_eta_cand_ML", "d0bar_eta_cand_ML" }); |
79 | | - auto h2 = combinatorics.Histo1D("delta_phi"); |
80 | | - auto h3 = combinatorics.Histo1D("delta_eta"); |
81 | | - |
82 | | - std::string rule = RCombinedDSIndexHelpers::combinationRuleAsString(combinationType); |
83 | | - h2->SetName(("DeltaPhi/" + rule).c_str()); |
84 | | - h2->Write(); |
85 | | - h3->SetName(("DeltaEta/" + rule).c_str()); |
86 | | - h3->Write(); |
87 | | - } |
88 | | - }; |
89 | | - } } } |
| 83 | + // FIXME: For the moment we hardcode saving the histograms. |
| 84 | + // In reality it should send the results as outputs to a downstream merger |
| 85 | + // process which merges them as wished. |
| 86 | + TFile f("result.root", "RECREATE"); |
| 87 | + h1->SetName("InvariantMass"); |
| 88 | + h1->Write(); |
| 89 | + h2->SetName("DeltaPhi"); |
| 90 | + h2->Write(); |
| 91 | + h3->SetName("DeltaEta"); |
| 92 | + h3->Write(); |
| 93 | + }) } } |
90 | 94 | }; |
91 | 95 | return workflow; |
92 | 96 | } |
0 commit comments