Skip to content

Commit

Permalink
Attribute support (#34)
Browse files Browse the repository at this point in the history
The missing attribute support is added for GenRunInfo, GenEvent, GenParticle, GenVertex. The following derived classes are added or completed:
- GenPdfInfo, GenHeavyIon, GenCrossSection, HEPRUPAttribute, HEPEUPAttribute

C++ Attributes are converted to native Python types were possible:
- all C++ primitives (numbers, bool) and std::string are mapped to corresponding python types
- std::vector of C++ primitives and string are mapped to list of corresponding python types
  • Loading branch information
HDembinski authored Sep 1, 2022
1 parent 54e089a commit 3fb41da
Show file tree
Hide file tree
Showing 26 changed files with 910 additions and 165 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: docs

on:
pull_request:
release:
types: [published]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 3
- uses: actions/setup-python@v2
with:
python-version: "3.9"
- run: python -m pip install -v -e .[doc]
- run: python docs/build.py
- uses: actions/upload-pages-artifact@v1
with:
path: 'docs/_build/html'

deploy:
if: github.ref_type == 'tag' || github.ref_name == 'main'
needs: build
# Set permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

runs-on: ubuntu-latest

steps:
- uses: actions/configure-pages@v2
- uses: actions/deploy-pages@v1
31 changes: 0 additions & 31 deletions .github/workflows/sphinx.yml

This file was deleted.

6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
[submodule "extern/HepMC3"]
path = extern/HepMC3
url = https://gitlab.cern.ch/hepmc/HepMC3.git
[submodule "extern/mp11"]
path = extern/mp11
url = https://github.com/boostorg/mp11.git
[submodule "extern/cpp-member-accessor"]
path = extern/cpp-member-accessor
url = https://github.com/hliberacki/cpp-member-accessor.git
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
endif()

set(CMAKE_CXX_STANDARD
11
14
CACHE STRING "C++ version selection")
set(CMAKE_CXX_STANDARD_REQUIRED ON) # optional, ensure standard is supported
set(CMAKE_CXX_EXTENSIONS OFF) # optional, keep compiler extensions off
Expand All @@ -27,6 +27,8 @@ file(GLOB SOURCES "src/*.cpp")
file(GLOB SOURCES2 "extern/HepMC3/src/*.cc")

pybind11_add_module(_core MODULE ${SOURCES} ${SOURCES2})
target_include_directories(_core PRIVATE extern/cpp-member-accessor/include)
target_include_directories(_core PRIVATE extern/mp11/include)
target_include_directories(_core PRIVATE extern/HepMC3/include)
target_compile_definitions(_core PRIVATE HepMC3_EXPORTS=1)

Expand Down
8 changes: 8 additions & 0 deletions docs/citation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Citation
========

If you use pyhepmc, please cite the HepMC3 library on which pyhepmc is build, and pyhepmc itself on Zenodo:

* `A. Buckley and others, Comput.Phys.Commun. 260 (2021) 107310 <https://doi.org/10.1016/j.cpc.2020.107310>`_

* `pyhepmc on Zenodo <https://doi.org/10.5281/zenodo.7013498>`_
10 changes: 9 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import pyhepmc
import os

project = "pyhepmc"
copyright = "2022, Hans Dembinski"
Expand All @@ -28,9 +29,16 @@
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output

html_theme = "classic"
html_static_path = ["_static"]

on_rtd = os.environ.get("READTHEDOCS", None) == "True"
if not on_rtd:
# Import and set the theme if we're building docs locally
import sphinx_rtd_theme

html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

# Autodoc options
autodoc_member_order = "groupwise"
autodoc_mock_imports = ["numpy", "particle", "graphviz"]
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
:hidden:

reference
citation
2 changes: 1 addition & 1 deletion extern/HepMC3
Submodule HepMC3 updated from c524bf to 591bcc
1 change: 1 addition & 0 deletions extern/cpp-member-accessor
Submodule cpp-member-accessor added at e72109
1 change: 1 addition & 0 deletions extern/mp11
Submodule mp11 added at f6133a
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ test =
particle
doc =
sphinx
sphinx-rtd-theme

[flake8]
max-line-length = 90
Expand Down
117 changes: 117 additions & 0 deletions src/attribute_conversion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include "pointer.hpp"
#include "pybind.hpp"
#include <HepMC3/Attribute.h>
#include <HepMC3/GenCrossSection.h>
#include <HepMC3/GenEvent.h>
#include <HepMC3/GenHeavyIon.h>
#include <HepMC3/GenPdfInfo.h>
#include <HepMC3/LHEFAttributes.h>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/list.hpp>
#include <boost/mp11/utility.hpp>
#include <stdexcept>

namespace HepMC3 {

py::object attribute_to_python(AttributePtr a) {
using namespace boost::mp11;

// this implementation must cover all C++ attribute types derived from Attribute

py::object result;

using RawTypes = mp_list<GenCrossSection, GenHeavyIon, GenPdfInfo, HEPRUPAttribute,
HEPEUPAttribute>;
// use mp_identity to make sure that default ctor is a noop
using Types = mp_transform<mp_identity, RawTypes>;
// this loop has exactly one match if any
mp_for_each<Types>([&](auto t) {
using AttributeType = typename decltype(t)::type;
if (auto x = std::dynamic_pointer_cast<AttributeType>(a)) result = py::cast(x);
});

if (!result) {

using RawTypes =
mp_list<IntAttribute, LongAttribute, DoubleAttribute, FloatAttribute,
StringAttribute, CharAttribute, LongLongAttribute, LongDoubleAttribute,
UIntAttribute, ULongLongAttribute, BoolAttribute, VectorCharAttribute,
VectorFloatAttribute, VectorLongDoubleAttribute,
VectorLongLongAttribute, VectorUIntAttribute, VectorULongAttribute,
VectorULongLongAttribute, VectorIntAttribute, VectorLongIntAttribute,
VectorDoubleAttribute, VectorStringAttribute>;

using Types = mp_transform<mp_identity, RawTypes>;
// this loop has exactly one match if any
mp_for_each<Types>([&](auto t) {
using AttributeType = typename decltype(t)::type;
if (auto x = std::dynamic_pointer_cast<AttributeType>(a))
result = py::cast(x->value());
});
}

if (!result) throw std::runtime_error("Attribute not convertible to Python type");
return result;
}

AttributePtr attribute_from_python(py::object obj) {
using namespace boost::mp11;

AttributePtr result;

if (py::isinstance<GenPdfInfo>(obj)) {
result = py::cast<GenPdfInfoPtr>(obj);
} else if (py::isinstance<GenHeavyIon>(obj)) {
result = py::cast<GenHeavyIonPtr>(obj);
} else if (py::isinstance<GenCrossSection>(obj)) {
result = py::cast<GenCrossSectionPtr>(obj);
} else if (py::isinstance<HEPRUPAttribute>(obj)) {
result = py::cast<HEPRUPAttributePtr>(obj);
} else if (py::isinstance<HEPEUPAttribute>(obj)) {
result = py::cast<HEPEUPAttributePtr>(obj);
}

if (!result) {
using Types = mp_list<mp_list<BoolAttribute, py::bool_, bool>,
mp_list<IntAttribute, py::int_, int>,
mp_list<FloatAttribute, py::float_, double>,
mp_list<StringAttribute, py::str, std::string>>;

mp_for_each<Types>([&](auto t) {
using T = decltype(t);
using AT = mp_at_c<T, 0>;
using PT = mp_at_c<T, 1>;
using CT = mp_at_c<T, 2>;
if (!result && py::isinstance<PT>(obj)) {
auto a = std::make_shared<AT>();
a->set_value(py::cast<CT>(obj));
result = a;
}
});
}

if (!result && py::isinstance<py::iterable>(obj) && py::len(obj) > 0) {
using Types = mp_list<mp_list<VectorIntAttribute, py::int_, int>,
mp_list<VectorDoubleAttribute, py::float_, double>,
mp_list<VectorStringAttribute, py::str, std::string>>;

py::int_ zero(0);
mp_for_each<Types>([&](auto t) {
using T = decltype(t);
using AT = mp_at_c<T, 0>;
using PT = mp_at_c<T, 1>;
using CT = std::vector<mp_at_c<T, 2>>;
auto item = py::reinterpret_borrow<py::object>(obj[zero]);
if (!result && py::isinstance<PT>(item)) {
auto a = std::make_shared<AT>();
a->set_value(py::cast<CT>(obj));
result = a;
}
});
}

if (!result) throw std::runtime_error("Python type not convertible to Attribute");
return result;
}

} // namespace HepMC3
91 changes: 91 additions & 0 deletions src/attributes_view.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "attributes_view.hpp"
#include "pointer.hpp"
#include "pybind.hpp"
#include <HepMC3/GenEvent.h>
#include <accessor/accessor.hpp>
#include <algorithm>
#include <map>
#include <pybind11/detail/common.h>
#include <pybind11/pytypes.h>
#include <pyerrors.h>
#include <set>
#include <sstream>
#include <string>

// To avoid the superfluous copy, we use the legal crowbar
// to access the private attribute map of GenEvent
MEMBER_ACCESSOR(MA1, HepMC3::GenEvent, m_attributes,
HepMC3::AttributesView::AttributeMap)

namespace HepMC3 {
py::object AttributesView::Iter::next() {
while (it_ != end_) {
auto& amap2 = it_->second;
if (amap2.find(id_) != amap2.end()) return py::cast(it_++->first);
++it_;
}
throw py::stop_iteration();
}

AttributesView::Iter AttributesView::iter() {
auto& amap = attributes();
return {amap.begin(), amap.end(), id_};
}

py::object AttributesView::getitem(py::str name) {
auto& amap = attributes();
auto it = amap.find(py::cast<std::string>(name));
if (it != amap.end()) {
auto& amap2 = it->second;
auto jt = amap2.find(id_);
if (jt != amap2.end()) return attribute_to_python(jt->second);
}
throw py::key_error(name);
return {};
}

void AttributesView::setitem(py::str name, py::object value) {
auto& amap = attributes();
auto& amap2 = amap[py::cast<std::string>(name)];
amap2.emplace(id_, attribute_from_python(value));
}

void AttributesView::delitem(py::str name) {
auto& amap = attributes();
auto it = amap.find(py::cast<std::string>(name));
if (it != amap.end()) {
auto& amap2 = it->second;
auto jt = amap2.find(id_);
if (jt != amap2.end()) {
amap2.erase(jt);
return;
}
}
throw py::key_error(name);
}

bool AttributesView::contains(py::str name) {
auto& amap = attributes();
auto it = amap.find(py::cast<std::string>(name));
if (it == amap.end()) return false;
auto& amap2 = it->second;
return amap2.find(id_) != amap2.end();
}

AttributesView::AttributeMap& AttributesView::attributes() {
auto ref = accessor::accessMember<MA1>(*event_);
return ref.get();
}

py::ssize_t AttributesView::len() {
py::size_t n = 0;
auto& amap = attributes();
for (auto& kv : amap) {
auto& amap2 = kv.second;
auto it = amap2.find(id_);
if (it != amap2.end()) ++n;
}
return n;
}

} // namespace HepMC3
Loading

0 comments on commit 3fb41da

Please sign in to comment.