Skip to content

Commit

Permalink
Adds GenEvent.from_hepevt (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
HDembinski authored Aug 21, 2022
1 parent ce009b3 commit 4d9ff79
Show file tree
Hide file tree
Showing 5 changed files with 332 additions and 56 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ python_requires = >=3.6

[options.extras_require]
test =
numpy
pytest
pytest-cov
graphviz
Expand Down
73 changes: 47 additions & 26 deletions src/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ bool operator!=(const GenParticle& a, const GenParticle& b) {

// compares all real qualities of both particle sets,
// but ignores the .id() fields and the particle order
bool equal_particle_set(const std::vector<ConstGenParticlePtr>& a,
const std::vector<ConstGenParticlePtr>& b) {
bool equal_particle_sets(const std::vector<ConstGenParticlePtr>& a,
const std::vector<ConstGenParticlePtr>& b) {
if (a.size() != b.size()) return false;
auto unmatched = b;
for (auto&& ai : a) {
Expand All @@ -82,16 +82,16 @@ bool equal_particle_set(const std::vector<ConstGenParticlePtr>& a,
// compares all real qualities of two vertices, but ignores the .id() field
bool operator==(const GenVertex& a, const GenVertex& b) {
return a.status() == b.status() && is_close(a.position(), b.position()) &&
equal_particle_set(a.particles_in(), b.particles_in()) &&
equal_particle_set(a.particles_out(), b.particles_out());
equal_particle_sets(a.particles_in(), b.particles_in()) &&
equal_particle_sets(a.particles_out(), b.particles_out());
}

bool operator!=(const GenVertex& a, const GenVertex& b) { return !operator==(a, b); }

// compares all real qualities of both vertex sets,
// but ignores the .id() fields and the vertex order
bool equal_vertex_set(const std::vector<ConstGenVertexPtr>& a,
const std::vector<ConstGenVertexPtr>& b) {
bool equal_vertex_sets(const std::vector<ConstGenVertexPtr>& a,
const std::vector<ConstGenVertexPtr>& b) {
if (a.size() != b.size()) return false;
auto unmatched = b;
for (auto&& ai : a) {
Expand Down Expand Up @@ -153,7 +153,7 @@ bool operator==(const GenEvent& a, const GenEvent& b) {
}

// if all vertices compare equal, then also all particles are equal
return equal_vertex_set(a.vertices(), b.vertices());
return equal_vertex_sets(a.vertices(), b.vertices());
}

bool operator!=(const GenEvent& a, const GenEvent& b) { return !operator==(a, b); }
Expand Down Expand Up @@ -263,6 +263,13 @@ inline std::ostream& repr(std::ostream& os, const HepMC3::GenEvent& x) {
repr(os, x.run_info()) << ")";
return os;
}

void from_hepevt(GenEvent& event, int event_number, py::array_t<double> px,
py::array_t<double> py, py::array_t<double> pz, py::array_t<double> en,
py::array_t<double> m, py::array_t<int> pid, py::array_t<int> status,
py::object parents, py::object children, py::object vx, py::object vy,
py::object vz, py::object vt);

} // namespace HepMC3

PYBIND11_MODULE(_core, m) {
Expand All @@ -279,7 +286,6 @@ PYBIND11_MODULE(_core, m) {
// GenEvent
// GenParticle
// GenVertex
// fill_genevent_from_hepevt
// print_hepevt
// print_content
// print_listing
Expand Down Expand Up @@ -433,8 +439,6 @@ PYBIND11_MODULE(_core, m) {
"run"_a, "momentum_unit"_a = Units::GEV, "length_unit"_a = Units::MM)
.def(py::init<Units::MomentumUnit, Units::LengthUnit>(),
"momentum_unit"_a = Units::GEV, "length_unit"_a = Units::MM)
PROP_RO_OL(particles, GenEvent, const std::vector<ConstGenParticlePtr>&)
PROP_RO_OL(vertices, GenEvent, const std::vector<ConstGenVertexPtr>&)
.def_property("weights",
overload_cast<std::vector<double>&, GenEvent>(&GenEvent::weights),
[](GenEvent& self, py::sequence seq) {
Expand Down Expand Up @@ -462,9 +466,6 @@ PYBIND11_MODULE(_core, m) {
"name"_a, "value"_a)
.def_property_readonly("weight_names",
[](const GenEvent& self) { return self.weight_names(); })
PROP(run_info, GenEvent) PROP(event_number, GenEvent)
PROP_RO(momentum_unit, GenEvent) PROP_RO(length_unit, GenEvent)
METH(set_units, GenEvent)
.def_property("heavy_ion",
overload_cast<GenHeavyIonPtr, GenEvent>(&GenEvent::heavy_ion),
&GenEvent::set_heavy_ion)
Expand All @@ -474,27 +475,44 @@ PYBIND11_MODULE(_core, m) {
.def_property(
"cross_section",
overload_cast<GenCrossSectionPtr, GenEvent>(&GenEvent::cross_section),
&GenEvent::set_cross_section) METH(event_pos, GenEvent)
PROP_RO_OL(beams, GenEvent, std::vector<ConstGenParticlePtr>)
METH_OL(add_vertex, GenEvent, void, GenVertexPtr)
METH_OL(add_particle, GenEvent, void, GenParticlePtr)
METH(set_beam_particles, GenEvent)
METH_OL(remove_vertex, GenEvent, void, GenVertexPtr)
METH_OL(remove_particle, GenEvent, void, GenParticlePtr)
&GenEvent::set_cross_section)
.def("reserve", &GenEvent::reserve, "particles"_a, "vertices"_a = 0)
METH(clear, GenEvent)
.def(py::self == py::self)
.def("__repr__",
[](GenEvent& self) {
std::ostringstream os;
repr(os, self);
return os.str();
})
.def("__str__", [](GenEvent& self) {
std::ostringstream os;
HepMC3::Print::listing(os, self, 2);
return os.str();
});
.def("__str__",
[](GenEvent& self) {
std::ostringstream os;
HepMC3::Print::listing(os, self, 2);
return os.str();
})

.def("from_hepevt", from_hepevt, "event_number"_a, "px"_a, "py"_a, "pz"_a, "en"_a,
"m"_a, "pid"_a, "status"_a, "parents"_a = py::none(),
"children"_a = py::none(), "vx"_a = py::none(), "vy"_a = py::none(),
"vz"_a = py::none(), "vt"_a = py::none())
// clang-format off
METH(clear, GenEvent)
PROP(run_info, GenEvent)
PROP(event_number, GenEvent)
PROP_RO(momentum_unit, GenEvent)
PROP_RO(length_unit, GenEvent)
METH(set_units, GenEvent)
METH(event_pos, GenEvent)
PROP_RO_OL(beams, GenEvent, std::vector<ConstGenParticlePtr>)
METH_OL(add_vertex, GenEvent, void, GenVertexPtr)
METH_OL(add_particle, GenEvent, void, GenParticlePtr)
METH(set_beam_particles, GenEvent)
METH_OL(remove_vertex, GenEvent, void, GenVertexPtr)
METH_OL(remove_particle, GenEvent, void, GenParticlePtr)
PROP_RO_OL(particles, GenEvent, const std::vector<ConstGenParticlePtr>&)
PROP_RO_OL(vertices, GenEvent, const std::vector<ConstGenVertexPtr>&)
// clang-format on
;

py::class_<GenParticle, GenParticlePtr>(m, "GenParticle")
.def(py::init<const FourVector&, int, int>(),
Expand Down Expand Up @@ -554,5 +572,8 @@ PYBIND11_MODULE(_core, m) {
return os.str();
});

FUNC(equal_particle_sets);
FUNC(equal_vertex_sets);

register_io(m);
}
182 changes: 182 additions & 0 deletions src/from_hepevt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#include "HepMC3/GenEvent.h"
#include "HepMC3/GenParticle.h"
#include "HepMC3/GenVertex.h"
#include "pybind.h"

#include <array>
#include <cassert>
#include <map>
#include <utility>
#include <vector>

// template <>
// struct std::hash<std::pair<int, int>> {
// std::size_t operator()(const std::pair<int, int>& p) const noexcept {
// auto h1 = std::hash<int>{}(p.first);
// auto h2 = std::hash<int>{}(p.second);
// return h1 ^ (h2 << 1); // or use boost::hash_combine
// }
// };

template <>
struct std::less<std::pair<int, int>> {
bool operator()(const std::pair<int, int>& a,
const std::pair<int, int>& b) const noexcept {
if (a.first == b.first) return a.second < b.second;
return a.first < b.first;
}
};

void normalize(int& m1, int& m2) {
// normalize mother range, see
// https://pythia.org/latest-manual/ParticleProperties.html
// m1 == m2 == 0 : no mothers
// m1 == m2 > 0 : elastic scattering
// m1 > 0, m2 == 0 : one mother
// m1 < m2, both > 0: interaction
// m2 < m1, both > 0: same, needs swapping

if (m1 > 0 && m2 > 0 && m2 < m1) std::swap(m1, m2);

if (m1 > 0 && m2 == 0) m2 = m1;

--m1; // fortran to c index
}

namespace HepMC3 {

void connect_parents_and_children(GenEvent& event, bool parents,
py::array_t<int> parents_or_children, py::object vx,
py::object vy, py::object vz, py::object vt) {

if (parents_or_children.request().ndim != 2)
throw std::runtime_error("parents or children must be 2D");

auto rco = parents_or_children.unchecked<2>();

const std::vector<GenParticlePtr>& particles = event.particles();
const int n = particles.size();
if (rco.shape(0) != n || rco.shape(1) != 2)
throw std::runtime_error("parents or children must have shape (N, 2)");

// find unique vertices:
// particles with same parents or children share one vertex
// if parents: map from parents to children
// if children: map from children to parents
std::map<std::pair<int, int>, std::vector<int>> vmap;
for (int i = 0; i < n; ++i) {
if (rco(i, 0) == 0 && rco(i, 1) == 0) continue;
vmap[std::make_pair(rco(i, 0), rco(i, 1))].push_back(i);
}

const int has_vertex = !vx.is_none() + !vy.is_none() + !vz.is_none() + !vt.is_none();

if (has_vertex && has_vertex != 4)
throw std::runtime_error("if one of vx, vy, vz, vt is set, all must be set");

py::array_t<double> avx;
py::array_t<double> avy;
py::array_t<double> avz;
py::array_t<double> avt;

if (has_vertex) {
avx = py::cast<py::array_t<double>>(vx);
avy = py::cast<py::array_t<double>>(vy);
avz = py::cast<py::array_t<double>>(vz);
avt = py::cast<py::array_t<double>>(vt);

if (avx.request().ndim != 1 || avy.request().ndim != 1 || avz.request().ndim != 1 ||
avt.request().ndim != 1)
throw std::runtime_error("vx, vy, vz, vt must be 1D");
}

auto x = avx.unchecked<1>();
auto y = avy.unchecked<1>();
auto z = avz.unchecked<1>();
auto t = avt.unchecked<1>();

if (has_vertex &&
(x.shape(0) != n || y.shape(0) != n || z.shape(0) != n || t.shape(0) != n))
throw std::runtime_error("vx, vy, vz, vt must have same length");

for (const auto& vi : vmap) {

int m1 = vi.first.first;
int m2 = vi.first.second;
// there must be at least one parent or child when we arrive here...
normalize(m1, m2);
assert(m1 < m2);

// ...with at least one child or parent
const auto& co = vi.second;
assert(!co.empty());

FourVector pos;
if (has_vertex) {
// we assume this is a production vertex
// if parent, co.front() is location of production vertex of first child
// if child, we use location of first child m1
const int i = parents ? co.front() : m1;
pos.set(x(i), y(i), z(i), t(i));
}

GenVertexPtr v{new GenVertex(pos)};

if (parents) {
for (int k = m1; k < m2; ++k) v->add_particle_in(particles.at(k));
for (const auto k : co) v->add_particle_out(particles.at(k));
} else {
for (int k = m1; k < m2; ++k) v->add_particle_out(particles.at(k));
for (const auto k : co) v->add_particle_in(particles.at(k));
}

event.add_vertex(v);
}
}

void from_hepevt(GenEvent& event, int event_number, py::array_t<double> px,
py::array_t<double> py, py::array_t<double> pz, py::array_t<double> en,
py::array_t<double> m, py::array_t<int> pid, py::array_t<int> status,
py::object parents, py::object children, py::object vx, py::object vy,
py::object vz, py::object vt) {
if (px.request().ndim != 1) throw std::runtime_error("px must be 1D");
if (py.request().ndim != 1) throw std::runtime_error("py must be 1D");
if (pz.request().ndim != 1) throw std::runtime_error("pz must be 1D");
if (en.request().ndim != 1) throw std::runtime_error("en must be 1D");
if (m.request().ndim != 1) throw std::runtime_error("m must be 1D");
if (pid.request().ndim != 1) throw std::runtime_error("pid must be 1D");
if (status.request().ndim != 1) throw std::runtime_error("status must be 1D");

auto rpx = px.unchecked<1>();
auto rpy = py.unchecked<1>();
auto rpz = pz.unchecked<1>();
auto ren = en.unchecked<1>();
auto rm = m.unchecked<1>();
auto rpid = pid.unchecked<1>();
auto rsta = status.unchecked<1>();

const int n = pid.shape(0);
if (rpx.shape(0) != n || rpy.shape(0) != n || rpz.shape(0) != n ||
ren.shape(0) != n || rm.shape(0) != n)
throw std::runtime_error("px, py, pz, en, m, pid, status must have same length");

event.clear();
event.reserve(n);
event.set_event_number(event_number);

for (int i = 0; i < n; ++i) {
GenParticlePtr p{
new GenParticle(FourVector(rpx(i), rpy(i), rpz(i), ren(i)), rpid(i), rsta(i))};
p->set_generated_mass(rm(i));
event.add_particle(p);
}

const bool have_parents = !parents.is_none();
const bool have_children = !children.is_none();
if (have_parents || have_children)
connect_parents_and_children(
event, have_parents,
py::cast<py::array_t<int>>(have_parents ? parents : children), vx, vy, vz, vt);
}

} // namespace HepMC3
2 changes: 1 addition & 1 deletion src/pybind.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef PYHEPMC_PYBIND_HEADER
#define PYHEPMC_PYBIND_HEADER

// #include <pybind11/numpy.h>
#include <pybind11/numpy.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
Expand Down
Loading

0 comments on commit 4d9ff79

Please sign in to comment.