Access method of lorentz 4-vector for elements of a RVec <ROOT::Math::PtEtaPhiEVector> from RDataFrame


I would like to use a method of a Lorentz 4-vector on a RVec ROOT::Math::PtEtaPhiEVector
I can easily do this on the leading RVec elements, but how can I do this on all elements

I can not figure out how to do this.
I wrote a small example where I calculate the invariant mass of all photon pairs where I explain the

#!/usr/bin/env python
import ROOT
import os,sys
#include c++ in python
using Vec_t = ROOT::VecOps::RVec<float>;
using Vec4_t = ROOT::Math::PtEtaPhiEVector;
ROOT::VecOps::RVec<  Vec4_t > ComputeInvariantMass(Vec_t& pt, Vec_t& eta, Vec_t& phi, Vec_t& e) {
  ROOT::VecOps::RVec< Vec4_t> pair;
  for (int i=0; i<pt.size(); i++) {
   for (int j=i+1; j<pt.size(); j++) {
     ROOT::Math::PtEtaPhiEVector p1(pt[i], eta[i], phi[i], e[i]);
     ROOT::Math::PtEtaPhiEVector p2(pt[j], eta[j], phi[j], e[j]);
 return pair;

if __name__ == "__main__":
 path = "root://"
 print ('filename= ',filename)
 d = ROOT.RDataFrame("mini", filename)
# taking the leading element works
# d = d.Define("photonmass", 'ComputeInvariantMass(photon_pt, photon_eta, photon_phi, photon_E)[0].mass()')
#how to calculate the invariant mass of each element in the vector
 d = d.Define("photonmass", 'ComputeInvariantMass(photon_pt, photon_eta, photon_phi, photon_E).mass()')
# this can not work like that since I need to access all elements of the RVEC and then execute the method 

 h1 = d.Histo1D("photonmass")

I am using root version 6.26/00 on lxplus7.

Thank you for your help.

Hi @tancredi ,

ComputeInvariantMass returns an RVec of 2 PtEtaPhiEVectors. You have a few options to transform that into an RVec of 2 invariant masses (i.e. 2 floats).

  1. you can do it directly in ComputeInvariantMass, e.g. by calling pair.emplace_back((p1+p2).mass()) instead of pair.emplace_back(p1 + p2)

  2. you can write a separate function that does the transformation, e.g. using the Map helper function from RVec, but you can also just do an explicit loop:

RVecD InvariantMasses(RVec<Vec4_t> &vecs) {
   return Map(vecs, [](Vec4_t &vec) { return vec.mass(); });
  1. similarly, you can use Map or an explicit loop inside the Define, e.g. (not using Map here just to show how else this could be done):
  'auto vecs = ComputeInvariantMass(photon_pt, photon_eta, photon_phi, photon_E); ROOT::RVecD masses(vecs.size()); for (int i = 0; i < vecs.size(); ++i) masses[i] = vecs.mass(); return masses;')

Personally I would go with option 2 and the Define expression can become InvariantMasses(Make4Vectors(photon_pt, photon_eta, photon_phi, photon_E)) (where I have renamed ComputeInvariantMass to Make4Vectors.

Side notes

  1. another possible implementation of ComputeInvariantMass that saves some redundant constructions of PtEtaPhiEVectors is:
auto vecs = Construct<Vec4_t>(pt, eta, phi, e); // creates an RVec<Vec4_t>
auto pair_idxs = Combinations(vecs, 2); // an RVec<RVec<size_t>> with a list of index pairs that span all pairs
RVec<Vec4_t> pairs;
pairs.reserve((pt.size() * pt.size() - 1) / 2);
for (auto idxs : pair_idxs)
  pairs.emplace_back(vecs[idxs[0]] + vecs[idxs[1]]);
return pairs;
  1. I would like to point out RVec’s helper functions InvariantMass and InvariantMasses in case they can be useful, maybe together with Combinations to get the indexes that form all possible pairs.