Skip to content

Commit

Permalink
feat: Track parameters json converter (#3724)
Browse files Browse the repository at this point in the history
Adding json converter for the different track parameters types. The
converter follows the pattern outlined in [nlohmann::json
documentation](https://json.nlohmann.me/features/arbitrary_types/#how-can-i-use-get-for-non-default-constructiblenon-copyable-types),
rather than the one present in the codebase due to the a) track
parameters types not being default constructible, b) use cases connected
to the grid-of-of-track-parameters conversion.

To keep the converter to one implementation of the json serializer, the
liberty was taken to add one more constructor to the
`FreeTrackParameters`. The `GridJsonConverter` code was modified a bit
to be able to handle non-default constructible types.

Header for the track parameters json converter in
`GridJsonConverter.hpp` is required for the grid-of-of-track-parameters
conversion to be possible by the `nlohmann::json` standard. As a side
note, all the types in the framework that have `to_json`/`from_json`
overloaded can have grid-of-type json conversion implemented for free,
if the respective headers are added to the `GridJsonConverter.hpp`.

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
ssdetlab and kodiakhq[bot] authored Oct 18, 2024
1 parent fbf332b commit c06a60a
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 6 deletions.
28 changes: 26 additions & 2 deletions Core/include/Acts/EventData/GenericFreeTrackParameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "Acts/EventData/detail/PrintParameters.hpp"
#include "Acts/Utilities/MathHelpers.hpp"
#include "Acts/Utilities/UnitVectors.hpp"
#include "Acts/Utilities/VectorHelpers.hpp"

#include <cassert>
#include <cmath>
Expand Down Expand Up @@ -56,6 +57,29 @@ class GenericFreeTrackParameters {
m_cov(std::move(cov)),
m_particleHypothesis(std::move(particleHypothesis)) {}

/// Construct from four-position, direction, absolute momentum, and charge.
///
/// @param pos4 Track position/time four-vector
/// @param dir Track direction three-vector; normalization is ignored.
/// @param qOverP Charge over momentum
/// @param cov Free parameters covariance matrix
/// @param particleHypothesis Particle hypothesis
GenericFreeTrackParameters(const Vector4& pos4, const Vector3& dir,
Scalar qOverP, std::optional<CovarianceMatrix> cov,
ParticleHypothesis particleHypothesis)
: m_params(FreeVector::Zero()),
m_cov(std::move(cov)),
m_particleHypothesis(std::move(particleHypothesis)) {
m_params[eFreePos0] = pos4[ePos0];
m_params[eFreePos1] = pos4[ePos1];
m_params[eFreePos2] = pos4[ePos2];
m_params[eFreeTime] = pos4[eTime];
m_params[eFreeDir0] = dir[eMom0];
m_params[eFreeDir1] = dir[eMom1];
m_params[eFreeDir2] = dir[eMom2];
m_params[eFreeQOverP] = qOverP;
}

/// Construct from four-position, angles, absolute momentum, and charge.
///
/// @param pos4 Track position/time four-vector
Expand Down Expand Up @@ -136,9 +160,9 @@ class GenericFreeTrackParameters {
Scalar time() const { return m_params[eFreeTime]; }

/// Phi direction.
Scalar phi() const { return phi(direction()); }
Scalar phi() const { return VectorHelpers::phi(direction()); }
/// Theta direction.
Scalar theta() const { return theta(direction()); }
Scalar theta() const { return VectorHelpers::theta(direction()); }
/// Charge over momentum.
Scalar qOverP() const { return m_params[eFreeQOverP]; }

Expand Down
11 changes: 7 additions & 4 deletions Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#pragma once

#include "Acts/Plugins/Json/ActsJson.hpp"
#include "Acts/Plugins/Json/TrackParametersJsonConverter.hpp"
#include "Acts/Utilities/AxisFwd.hpp"
#include "Acts/Utilities/GridAccessHelpers.hpp"
#include "Acts/Utilities/IAxis.hpp"
Expand Down Expand Up @@ -266,15 +267,17 @@ auto fromJson(const nlohmann::json& jGrid,
if constexpr (GridType::DIM == 1u) {
for (const auto& jd : jData) {
std::array<std::size_t, 1u> lbin = jd[0u];
value_type values = jd[1u];
grid.atLocalBins(lbin) = values;
if (!jd[1u].is_null()) {
grid.atLocalBins(lbin) = jd[1u].get<value_type>();
}
}
}
if constexpr (GridType::DIM == 2u) {
for (const auto& jd : jData) {
std::array<std::size_t, 2u> lbin = jd[0u];
value_type values = jd[1u];
grid.atLocalBins(lbin) = values;
if (!jd[1u].is_null()) {
grid.atLocalBins(lbin) = jd[1u].get<value_type>();
}
}
}
return grid;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// This file is part of the ACTS project.
//
// Copyright (C) 2016 CERN for the benefit of the ACTS project
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

#pragma once

#include "Acts/EventData/TrackParameters.hpp"
#include "Acts/Plugins/Json/ActsJson.hpp"
#include "Acts/Plugins/Json/SurfaceJsonConverter.hpp"

#include <nlohmann/json.hpp>

namespace {

// Alias to bound adl_serializer specialization
// only to track parameters
template <class parameters_t>
concept TrackParameters = Acts::FreeTrackParametersConcept<parameters_t> ||
Acts::BoundTrackParametersConcept<parameters_t>;

// Shorthand for bound track parameters
template <class parameters_t>
concept IsGenericBound =
std::same_as<parameters_t, Acts::GenericBoundTrackParameters<
typename parameters_t::ParticleHypothesis>>;

} // namespace

namespace Acts {
NLOHMANN_JSON_SERIALIZE_ENUM(Acts::PdgParticle,

{{Acts::PdgParticle::eInvalid, "Invalid"},
{Acts::PdgParticle::eElectron, "Electron"},
{Acts::PdgParticle::eAntiElectron,
"AntiElectron"},
{Acts::PdgParticle::ePositron, "Positron"},
{Acts::PdgParticle::eMuon, "Muon"},
{Acts::PdgParticle::eAntiMuon, "AntiMuon"},
{Acts::PdgParticle::eTau, "Tau"},
{Acts::PdgParticle::eAntiTau, "AntiTau"},
{Acts::PdgParticle::eGamma, "Gamma"},
{Acts::PdgParticle::ePionZero, "PionZero"},
{Acts::PdgParticle::ePionPlus, "PionPlus"},
{Acts::PdgParticle::ePionMinus, "PionMinus"},
{Acts::PdgParticle::eKaonPlus, "KaonPlus"},
{Acts::PdgParticle::eKaonMinus, "KaonMinus"},
{Acts::PdgParticle::eNeutron, "Neutron"},
{Acts::PdgParticle::eAntiNeutron, "AntiNeutron"},
{Acts::PdgParticle::eProton, "Proton"},
{Acts::PdgParticle::eAntiProton, "AntiProton"},
{Acts::PdgParticle::eLead, "Lead"}}

)
}

namespace nlohmann {

/// @brief Serialize a track parameters object to json
///
/// nlohmann::json serializer specialized for track parameters
/// as they are not default constructible. Is able to serialize
/// either bound or free track parameters given that the constructor
/// convention is followed.
///
/// @tparam parameters_t The track parameters type
template <TrackParameters parameters_t>
struct adl_serializer<parameters_t> {
/// Covariance matrix type attached to the parameters
using CovarianceMatrix = typename parameters_t::CovarianceMatrix;

/// @brief Serialize track parameters object to json
///
/// @param j Json object to write to
/// @param t Track parameters object to serialize
static void to_json(nlohmann::json& j, const parameters_t& t) {
// Serialize parameters
// common to all track parameters
j["direction"] = t.direction();
j["qOverP"] = t.qOverP();
j["particleHypothesis"] = t.particleHypothesis().absolutePdg();

// Covariance is optional
j["covariance"];
if (t.covariance().has_value()) {
// Extract covariance matrix
// parameters and serialize
auto cov = t.covariance().value();
constexpr unsigned int size = cov.rows();
std::array<Acts::ActsScalar, size * size> covData{};
for (std::size_t n = 0; n < size; ++n) {
for (std::size_t m = 0; m < size; ++m) {
covData[n * size + m] = cov(n, m);
}
}
j["covariance"] = covData;
}
// Bound track parameters have
// reference surface attached
// and position takes a geometry context
if constexpr (IsGenericBound<parameters_t>) {
Acts::GeometryContext gctx;
j["position"] = t.fourPosition(gctx);

j["referenceSurface"] =
Acts::SurfaceJsonConverter::toJson(gctx, t.referenceSurface());
} else {
j["position"] = t.fourPosition();
}
}

/// @brief Deserialize track parameters object from json
///
/// @param j Json object to read from
/// @return Track parameters object
static parameters_t from_json(const nlohmann::json& j) {
// Extract common parameters
std::array<Acts::ActsScalar, 4> posData = j.at("position");
Acts::Vector4 position(posData[0], posData[1], posData[2], posData[3]);

std::array<Acts::ActsScalar, 3> dirData = j.at("direction");
Acts::Vector3 direction(dirData[0], dirData[1], dirData[2]);

Acts::ActsScalar qOverP = j.at("qOverP");
Acts::PdgParticle absPdg = j.at("particleHypothesis");

// Covariance is optional
std::optional<CovarianceMatrix> cov;
if (j.at("covariance").is_null()) {
cov = std::nullopt;
} else {
// Extract covariance matrix
// parameters and deserialize
CovarianceMatrix mat;
constexpr unsigned int size = mat.rows();
std::array<Acts::ActsScalar, size * size> covData = j.at("covariance");
for (std::size_t n = 0; n < size; ++n) {
for (std::size_t m = 0; m < size; ++m) {
mat(n, m) = covData[n * size + m];
}
}
cov.emplace(std::move(mat));
}

// Create particle hypothesis
typename parameters_t::ParticleHypothesis particle(absPdg);

// Bound track parameters have
// reference surface attached
// and constructor is hidden
// behind a factory method
if constexpr (IsGenericBound<parameters_t>) {
Acts::GeometryContext gctx;
auto referenceSurface =
Acts::SurfaceJsonConverter::fromJson(j.at("referenceSurface"));

auto res = parameters_t::create(referenceSurface, gctx, position,
direction, qOverP, cov, particle);

if (!res.ok()) {
throw std::invalid_argument("Invalid bound track parameters");
}
return res.value();
} else {
return parameters_t(position, direction, qOverP, cov, particle);
}
}
};

/// @brief Serialize a shared pointer to track parameters object to json
///
/// nlohmann::json serializer specialized for shared pointers to track
/// parameters as they are not default constructible. Is able to serialize
/// either bound or free track parameters given that the constructor
/// convention is followed.
///
/// @tparam parameters_t The track parameters type
template <TrackParameters parameters_t>
struct adl_serializer<std::shared_ptr<parameters_t>> {
using CovarianceMatrix = typename parameters_t::CovarianceMatrix;
static void to_json(nlohmann::json& j,
const std::shared_ptr<parameters_t>& t) {
if (t == nullptr) {
return;
}
j = *t;
}

static std::shared_ptr<parameters_t> from_json(const nlohmann::json& j) {
return std::make_shared<parameters_t>(j.get<parameters_t>());
}
};

/// @brief Serialize a unique pointer to track parameters object to json
///
/// nlohmann::json serializer specialized for unique pointers to track
/// parameters as they are not default constructible. Is able to serialize
/// either bound or free track parameters given that the constructor
/// convention is followed.
///
/// @tparam parameters_t The track parameters type
template <TrackParameters parameters_t>
struct adl_serializer<std::unique_ptr<parameters_t>> {
using CovarianceMatrix = typename parameters_t::CovarianceMatrix;
static void to_json(nlohmann::json& j,
const std::unique_ptr<parameters_t>& t) {
if (t == nullptr) {
return;
}
j = *t;
}

static std::unique_ptr<parameters_t> from_json(const nlohmann::json& j) {
return std::make_unique<parameters_t>(j.get<parameters_t>());
}
};

} // namespace nlohmann
1 change: 1 addition & 0 deletions Tests/UnitTests/Plugins/Json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ add_unittest(UtilitiesJsonConverter UtilitiesJsonConverterTests.cpp)
add_unittest(SurfaceBoundsJsonConverter SurfaceBoundsJsonConverterTests.cpp)
add_unittest(SurfaceJsonConverter SurfaceJsonConverterTests.cpp)
add_unittest(VolumeBoundsJsonConverter VolumeBoundsJsonConverterTests.cpp)
add_unittest(TrackParametersJsonConverter TrackParametersJsonConverterTests.cpp)
Loading

0 comments on commit c06a60a

Please sign in to comment.