Skip to content

Commit dc27c95

Browse files
Adapt to zero based indices (#69)
The method `GenEvent.from_hepevt` now has an additional argument `fortran` which allows one to define whether the input uses 1-based indices or 0-based indices. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent ca91fcf commit dc27c95

File tree

10 files changed

+116
-56
lines changed

10 files changed

+116
-56
lines changed

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ repos:
3535

3636
# Python formatting
3737
- repo: https://github.com/psf/black-pre-commit-mirror
38-
rev: 23.1.0
38+
rev: 23.7.0
3939
hooks:
4040
- id: black
4141

@@ -48,14 +48,14 @@ repos:
4848

4949
# Python linter (Flake8)
5050
- repo: https://github.com/pycqa/flake8
51-
rev: 6.0.0
51+
rev: 6.1.0
5252
hooks:
5353
- id: flake8
5454
exclude: ^(docs/.*|tests/.*)$
5555

5656
# C++ formatting
5757
- repo: https://github.com/pre-commit/mirrors-clang-format
58-
rev: v15.0.7
58+
rev: v16.0.6
5959
hooks:
6060
- id: clang-format
6161

@@ -70,7 +70,7 @@ repos:
7070

7171
# Python type checking
7272
- repo: https://github.com/pre-commit/mirrors-mypy
73-
rev: 'v0.991'
73+
rev: 'v1.5.1'
7474
hooks:
7575
- id: mypy
7676
# additional_dependencies: [numpy]

docs/examples/basics.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"cells": [
2+
"cells": [
33
{
44
"cell_type": "markdown",
55
"metadata": {},

docs/examples/processing.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"cells": [
2+
"cells": [
33
{
44
"attachments": {},
55
"cell_type": "markdown",

src/core.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ void from_hepevt(GenEvent& event, int event_number, py::array_t<double> px,
6363
py::array_t<double> py, py::array_t<double> pz, py::array_t<double> en,
6464
py::array_t<double> m, py::array_t<int> pid, py::array_t<int> status,
6565
py::object parents, py::object children, py::object vx, py::object vy,
66-
py::object vz, py::object vt);
66+
py::object vz, py::object vt, bool fortran);
6767

6868
} // namespace HepMC3
6969

@@ -522,7 +522,8 @@ PYBIND11_MODULE(_core, m) {
522522
.def("from_hepevt", from_hepevt, "event_number"_a, "px"_a, "py"_a, "pz"_a, "en"_a,
523523
"m"_a, "pid"_a, "status"_a, "parents"_a = py::none(),
524524
"children"_a = py::none(), "vx"_a = py::none(), "vy"_a = py::none(),
525-
"vz"_a = py::none(), "vt"_a = py::none(), DOC(GenEvent.from_hepevt))
525+
"vz"_a = py::none(), "vt"_a = py::none(), "fortran"_a = true,
526+
DOC(GenEvent.from_hepevt))
526527
.def("write_data", &GenEvent::write_data, "data"_a, DOC(GenEvent.write_data))
527528
.def("read_data", &GenEvent::read_data, "data"_a, DOC(GenEvent.read_data))
528529
.def_property_readonly("numpy", [](py::object self) { return NumpyAPI(self); })

src/from_hepevt.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,33 @@ struct std::less<std::pair<int, int>> {
1818
}
1919
};
2020

21-
void normalize(int& m1, int& m2) {
21+
void normalize(int& m1, int& m2, bool fortran) {
2222
// normalize mother range, see
2323
// https://pythia.org/latest-manual/ParticleProperties.html
24-
// m1 == m2 == 0 : no mothers
25-
// m1 == m2 > 0 : elastic scattering
26-
// m1 > 0, m2 == 0 : one mother
27-
// m1 < m2, both > 0: interaction
28-
// m2 < m1, both > 0: same, needs swapping
29-
30-
if (m1 > 0 && m2 == 0)
24+
// m1 == m2 == 0 : no mothers
25+
// m1 == m2 > 0 : elastic scattering
26+
// m1 > 0, m2 == 0 : one mother
27+
// m1 < m2, both > 0: interaction
28+
// m2 < m1, both > 0: same, needs swapping
29+
// If the input is coming from C, all indices one less.
30+
// After normalization, we want m1, m2 to be a valid
31+
// c-style integer range, where m2 points one past the
32+
// last included index or m1 == m2 (empty range).
33+
34+
const int invalid = fortran ? 0 : -1;
35+
36+
if (m1 > invalid && m2 == invalid)
3137
m2 = m1;
3238
else if (m2 < m1)
3339
std::swap(m1, m2);
3440

35-
--m1; // fortran to c index
41+
if (fortran)
42+
--m1;
43+
else
44+
++m2;
45+
46+
// postcondition after normalize
47+
assert((m1 == 0 && m2 == 0) || m1 < m2);
3648
}
3749

3850
namespace HepMC3 {
@@ -52,7 +64,8 @@ void modded_add_particle_in(int vid, GenVertexPtr v, GenParticlePtr p) {
5264

5365
void connect_parents_and_children(GenEvent& event, bool parents,
5466
py::array_t<int> parents_or_children, py::object vx,
55-
py::object vy, py::object vz, py::object vt) {
67+
py::object vy, py::object vz, py::object vt,
68+
bool fortran) {
5669

5770
if (parents_or_children.request().ndim != 2)
5871
throw std::runtime_error("parents or children must be 2D");
@@ -69,8 +82,9 @@ void connect_parents_and_children(GenEvent& event, bool parents,
6982
// if parents: map from parents to children
7083
// if children: map from children to parents
7184
std::map<std::pair<int, int>, std::vector<int>> vmap;
85+
const int invalid = fortran ? 0 : -1;
7286
for (int i = 0; i < n; ++i) {
73-
if (rco(i, 0) == 0 && rco(i, 1) == 0) continue;
87+
if (rco(i, 0) <= invalid && rco(i, 1) <= invalid) continue;
7488
vmap[std::make_pair(rco(i, 0), rco(i, 1))].push_back(i);
7589
}
7690

@@ -110,8 +124,7 @@ void connect_parents_and_children(GenEvent& event, bool parents,
110124
int m2 = vi.first.second;
111125

112126
// there must be at least one parent or child when we arrive here...
113-
normalize(m1, m2);
114-
assert(m1 < m2); // postcondition after normalize
127+
normalize(m1, m2, fortran);
115128

116129
if (m1 < 0 || m2 > n) {
117130
std::ostringstream os;
@@ -158,7 +171,7 @@ void from_hepevt(GenEvent& event, int event_number, py::array_t<double> px,
158171
py::array_t<double> py, py::array_t<double> pz, py::array_t<double> en,
159172
py::array_t<double> m, py::array_t<int> pid, py::array_t<int> status,
160173
py::object parents, py::object children, py::object vx, py::object vy,
161-
py::object vz, py::object vt) {
174+
py::object vz, py::object vt, bool fortran) {
162175
if (px.request().ndim != 1) throw std::runtime_error("px must be 1D");
163176
if (py.request().ndim != 1) throw std::runtime_error("py must be 1D");
164177
if (pz.request().ndim != 1) throw std::runtime_error("pz must be 1D");
@@ -196,7 +209,8 @@ void from_hepevt(GenEvent& event, int event_number, py::array_t<double> px,
196209
if (have_parents || have_children)
197210
connect_parents_and_children(
198211
event, have_parents,
199-
py::cast<py::array_t<int>>(have_parents ? parents : children), vx, vy, vz, vt);
212+
py::cast<py::array_t<int>>(have_parents ? parents : children), vx, vy, vz, vt,
213+
fortran);
200214
}
201215

202216
} // namespace HepMC3

src/numpy_api.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,28 @@ void register_numpy_api(py::module& m) {
2626
py::class_<ParticlesAPI>(m, "ParticlesAPI")
2727
.def(py::init<py::object>())
2828
// clang-format off
29-
NP_ARRAY(ParticlesAPI, particles, int, id)
30-
NP_ARRAY(ParticlesAPI, particles, int, pid)
31-
NP_ARRAY(ParticlesAPI, particles, int, status)
32-
NP_ARRAY(ParticlesAPI, particles, bool, is_generated_mass_set)
33-
NP_ARRAY(ParticlesAPI, particles, double, generated_mass)
34-
NP_ARRAY2(ParticlesAPI, particles, double, momentum, px)
35-
NP_ARRAY2(ParticlesAPI, particles, double, momentum, py)
36-
NP_ARRAY2(ParticlesAPI, particles, double, momentum, pz)
37-
NP_ARRAY2(ParticlesAPI, particles, double, momentum, e)
29+
NP_ARRAY(ParticlesAPI, particles, int, id)
30+
NP_ARRAY(ParticlesAPI, particles, int, pid)
31+
NP_ARRAY(ParticlesAPI, particles, int, status)
32+
NP_ARRAY(ParticlesAPI, particles, bool, is_generated_mass_set)
33+
NP_ARRAY(ParticlesAPI, particles, double, generated_mass)
34+
NP_ARRAY2(ParticlesAPI, particles, double, momentum, px)
35+
NP_ARRAY2(ParticlesAPI, particles, double, momentum, py)
36+
NP_ARRAY2(ParticlesAPI, particles, double, momentum, pz)
37+
NP_ARRAY2(ParticlesAPI, particles, double, momentum, e)
3838
// clang-format on
3939
;
4040

4141
py::class_<VerticesAPI>(m, "VerticesAPI")
4242
.def(py::init<py::object>())
4343
// clang-format off
44-
NP_ARRAY(VerticesAPI, vertices, int, id)
45-
NP_ARRAY(VerticesAPI, vertices, int, status)
46-
NP_ARRAY(VerticesAPI, vertices, bool, has_set_position)
47-
NP_ARRAY2(VerticesAPI, vertices, double, position, x)
48-
NP_ARRAY2(VerticesAPI, vertices, double, position, y)
49-
NP_ARRAY2(VerticesAPI, vertices, double, position, z)
50-
NP_ARRAY2(VerticesAPI, vertices, double, position, t)
44+
NP_ARRAY(VerticesAPI, vertices, int, id)
45+
NP_ARRAY(VerticesAPI, vertices, int, status)
46+
NP_ARRAY(VerticesAPI, vertices, bool, has_set_position)
47+
NP_ARRAY2(VerticesAPI, vertices, double, position, x)
48+
NP_ARRAY2(VerticesAPI, vertices, double, position, y)
49+
NP_ARRAY2(VerticesAPI, vertices, double, position, z)
50+
NP_ARRAY2(VerticesAPI, vertices, double, position, t)
5151
// clang-format on
5252
;
5353

src/pyhepmc/_doc.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@
5454
status: array-like
5555
Status codes of particles.
5656
parents: array-like or None, optional
57-
Array of int with shape (N, 2). Start and stop index (inclusive) in the record for the parents of each particle in Fortran numbering (starting with 1). No parents for a particle are indicated by the pair (0, 0) or (-1, -1). Single parents are indicated either by (N, 0) or (N, N) with N > 0.
57+
Array of int with shape (N, 2). Start and stop index (inclusive) in the record
58+
for the parents of each particle. Indices must be zero-based (C) or one-based
59+
(Fortran). In the latter case, keyword `subtract_one` must be set to true. No
60+
parents for a particle are indicated by the pair (-1, -1). Single parents are
61+
indicated either by (N, -1) or (N, N) with N > 0.
5862
children: array-like or None, optional
5963
Like parents but for the children of this particle.
6064
vx : array-like or None, optional
@@ -65,6 +69,9 @@
6569
Z-component of location of production vertex of particles in mm.
6670
vt : array-like or None, optional
6771
Time (ct) of production vertex of particles in mm.
72+
fortran : bool, optional
73+
If True (default), the source indices are 1-based (Fortran, Pythia8). Set this
74+
to False, if the indices are 0-based (C-style).
6875
""",
6976
"GenEvent.weight": """Get event weight accessed by index (or the canonical/first one if there is no argument) or name.
7077

src/pyhepmc/io.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,9 @@ class HepMCFile:
212212
IOError if reading or writing fails.
213213
"""
214214

215+
_reader: Optional[ReaderMixin]
216+
_writer: Optional[Union[WriterAscii, WriterAsciiHepMC2, WriterHEPEVT]]
217+
215218
def __init__(
216219
self,
217220
fileobj: Filename,
@@ -246,8 +249,7 @@ def __init__(
246249

247250
mode += "b"
248251

249-
def open_file() -> Any:
250-
return open(fn, mode)
252+
open_file = lambda: open(fn, mode) # noqa: E731
251253

252254
self._close_file = True
253255

@@ -310,6 +312,7 @@ def open_file() -> Any:
310312
__exit__ = _exit_close
311313

312314
def __iter__(self) -> Any:
315+
assert self._reader is not None
313316
return self._reader.__iter__()
314317

315318
def flush(self) -> None:

tests/test_basic.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
import pyhepmc as hep
33
from numpy.testing import assert_equal
4+
import numpy as np
45

56

67
def make_evt():
@@ -325,7 +326,8 @@ def test_GenEvent_generated_mass():
325326

326327

327328
@pytest.mark.parametrize("use_parent", (True, False))
328-
def test_GenEvent_from_hepevt(use_parent, evt):
329+
@pytest.mark.parametrize("fortran", (True, False))
330+
def test_GenEvent_from_hepevt(use_parent, fortran, evt):
329331
status = [p.status for p in evt.particles]
330332
pid = [p.pid for p in evt.particles]
331333
px = [p.momentum[0] for p in evt.particles]
@@ -346,9 +348,14 @@ def test_GenEvent_from_hepevt(use_parent, evt):
346348
# / p6
347349
# p2
348350

351+
# fortran style
349352
parents = [(0, 0), (0, 0), (1, 1), (2, 2), (3, 4), (3, 4), (5, 5), (5, 5)]
350353
children = [(3, 0), (4, 0), (5, 6), (5, 6), (7, 8), (0, 0), (0, 0), (0, 0)]
351354

355+
if not fortran:
356+
parents = np.subtract(parents, 1)
357+
children = np.subtract(children, 1)
358+
352359
ev = hep.GenEvent()
353360
if use_parent:
354361
ev.from_hepevt(
@@ -366,6 +373,7 @@ def test_GenEvent_from_hepevt(use_parent, evt):
366373
vy,
367374
vz,
368375
vt,
376+
fortran=fortran,
369377
)
370378
else:
371379
ev.from_hepevt(
@@ -383,6 +391,7 @@ def test_GenEvent_from_hepevt(use_parent, evt):
383391
vy,
384392
vz,
385393
vt,
394+
fortran=fortran,
386395
)
387396

388397
# cannot be taken from HepEvt record, but is set for evt

0 commit comments

Comments
 (0)