Skip to content

Commit

Permalink
feat: adding json conversion and unit test for ProtoAxis (#4045)
Browse files Browse the repository at this point in the history
This PR adds json writing/reading to the new ProtoAxis. It supersedes #4038
  • Loading branch information
asalzburger authored Jan 24, 2025
1 parent 0340320 commit 429e34d
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 0 deletions.
1 change: 1 addition & 0 deletions Plugins/Json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_library(
src/MaterialJsonConverter.cpp
src/PortalJsonConverter.cpp
src/ProtoDetectorJsonConverter.cpp
src/ProtoAxisJsonConverter.cpp
src/SurfaceBoundsJsonConverter.cpp
src/SurfaceJsonConverter.cpp
src/UtilitiesJsonConverter.cpp
Expand Down
34 changes: 34 additions & 0 deletions Plugins/Json/include/Acts/Plugins/Json/ProtoAxisJsonConverter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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/Plugins/Json/ActsJson.hpp"
#include "Acts/Utilities/ProtoAxis.hpp"

#include <nlohmann/json.hpp>

/// Custom Json encoder/decoders. Naming is mandated by nlohmann::json and thus
/// can not match our naming guidelines.
///
/// This uses a custom API and nomenclature as it would
/// otherwise require the ProtoAxis to have a default
/// constructor which is deleted
namespace Acts::ProtoAxisJsonConverter {

/// Write the ProtoAxis to a json object
///
/// @param pa the proto axis to be written out
nlohmann::json toJson(const ProtoAxis& pa);

/// Create a ProtoAxis from a json object
///
/// @param j the json object to be read from
Acts::ProtoAxis fromJson(const nlohmann::json& j);

} // namespace Acts::ProtoAxisJsonConverter
53 changes: 53 additions & 0 deletions Plugins/Json/src/ProtoAxisJsonConverter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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/.

#include "Acts/Plugins/Json/ProtoAxisJsonConverter.hpp"

#include "Acts/Plugins/Json/GridJsonConverter.hpp"
#include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp"
#include "Acts/Utilities/AxisDefinitions.hpp"

nlohmann::json Acts::ProtoAxisJsonConverter::toJson(const Acts::ProtoAxis& pa) {
nlohmann::json j;
j["axis_dir"] = pa.getAxisDirection();
j["axis"] = AxisJsonConverter::toJson(pa.getAxis());
j["autorange"] = pa.isAutorange();
return j;
}

Acts::ProtoAxis Acts::ProtoAxisJsonConverter::fromJson(
const nlohmann::json& j) {
auto axisDir = j.at("axis_dir").get<Acts::AxisDirection>();
auto axisBoundaryType =
j.at("axis").at("boundary_type").get<Acts::AxisBoundaryType>();
if (auto axisType = j.at("axis").at("type").get<Acts::AxisType>();
axisType == AxisType::Equidistant) {
auto nbins = j.at("axis").at("bins").get<std::size_t>();
if (nbins == 0) {
throw std::invalid_argument("Number of bins must be positive");
}

if (j.at("autorange").get<bool>()) {
return ProtoAxis(axisDir, axisBoundaryType, nbins);
}
auto min = j.at("axis").at("range").at(0).get<double>();
auto max = j.at("axis").at("range").at(1).get<double>();
if (min >= max) {
throw std::invalid_argument("Invalid range: min must be less than max");
}
return ProtoAxis(axisDir, axisBoundaryType, min, max, nbins);
}
auto binEdges = j.at("axis").at("boundaries").get<std::vector<double>>();
if (binEdges.size() < 2) {
throw std::invalid_argument("At least two bin edges required");
}
if (!std::ranges::is_sorted(binEdges)) {
throw std::invalid_argument("Bin edges must be sorted in ascending order");
}
return ProtoAxis(axisDir, axisBoundaryType, binEdges);
}
1 change: 1 addition & 0 deletions Tests/UnitTests/Plugins/Json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_unittest(GridJsonConverter GridJsonConverterTests.cpp)
add_unittest(MaterialJsonConverter MaterialJsonConverterTests.cpp)
add_unittest(MaterialMapJsonConverter MaterialMapJsonConverterTests.cpp)
add_unittest(PortalJsonConverter PortalJsonConverterTests.cpp)
add_unittest(ProtoAxisJsonConverter ProtoAxisJsonConverterTests.cpp)
add_unittest(ProtoDetectorJsonConverter ProtoDetectorJsonConverterTests.cpp)
add_unittest(UtilitiesJsonConverter UtilitiesJsonConverterTests.cpp)
add_unittest(SurfaceBoundsJsonConverter SurfaceBoundsJsonConverterTests.cpp)
Expand Down
155 changes: 155 additions & 0 deletions Tests/UnitTests/Plugins/Json/ProtoAxisJsonConverterTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// 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/.

#include <boost/test/data/test_case.hpp>
#include <boost/test/unit_test.hpp>

#include "Acts/Plugins/Json/ActsJson.hpp"
#include "Acts/Plugins/Json/ProtoAxisJsonConverter.hpp"
#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
#include "Acts/Utilities/Axis.hpp"
#include "Acts/Utilities/AxisDefinitions.hpp"
#include "Acts/Utilities/ProtoAxis.hpp"

BOOST_AUTO_TEST_SUITE(ProtoAxisJsonConversion)

BOOST_AUTO_TEST_CASE(EquidistantProtoAxisJsonConversion) {
using enum Acts::AxisBoundaryType;
using enum Acts::AxisDirection;
using enum Acts::AxisType;

// Bound, equidistant axis
Acts::ProtoAxis epab(AxisX, Bound, 0.0, 1.0, 10);

nlohmann::json jProtoAxis = Acts::ProtoAxisJsonConverter::toJson(epab);

BOOST_CHECK(jProtoAxis.contains("axis"));
BOOST_CHECK(jProtoAxis.contains("axis_dir"));
BOOST_CHECK(jProtoAxis.contains("autorange"));

Acts::ProtoAxis epabRead = Acts::ProtoAxisJsonConverter::fromJson(jProtoAxis);

BOOST_CHECK_EQUAL(epabRead.getAxisDirection(), epab.getAxisDirection());
BOOST_CHECK_EQUAL(epabRead.getAxis(), epab.getAxis());
BOOST_CHECK_EQUAL(epabRead.isAutorange(), epab.isAutorange());
BOOST_CHECK_EQUAL(epabRead.toString(), epab.toString());
}

BOOST_AUTO_TEST_CASE(AutorangeProtoAxisJsonConversion) {
using enum Acts::AxisBoundaryType;
using enum Acts::AxisDirection;
using enum Acts::AxisType;

// Bound, equidistant axis, autorange
Acts::ProtoAxis epa(AxisX, Bound, 10);

nlohmann::json jProtoAxis = Acts::ProtoAxisJsonConverter::toJson(epa);

BOOST_CHECK(jProtoAxis.contains("axis"));
BOOST_CHECK(jProtoAxis.contains("axis_dir"));
BOOST_CHECK(jProtoAxis.contains("autorange"));

Acts::ProtoAxis epaRead = Acts::ProtoAxisJsonConverter::fromJson(jProtoAxis);

BOOST_CHECK_EQUAL(epaRead.getAxisDirection(), epa.getAxisDirection());
BOOST_CHECK_EQUAL(epaRead.getAxis(), epa.getAxis());
BOOST_CHECK_EQUAL(epaRead.isAutorange(), epa.isAutorange());
BOOST_CHECK_EQUAL(epaRead.toString(), epa.toString());
}

BOOST_AUTO_TEST_CASE(VariableProtoAxisJsonConversion) {
using enum Acts::AxisBoundaryType;
using enum Acts::AxisDirection;
using enum Acts::AxisType;

// Bound, variable axis
Acts::ProtoAxis vpab(AxisX, Bound, {0.0, 1.0, 10});

nlohmann::json jProtoAxis = Acts::ProtoAxisJsonConverter::toJson(vpab);
BOOST_CHECK(jProtoAxis.contains("axis"));
BOOST_CHECK(jProtoAxis.contains("axis_dir"));
BOOST_CHECK(jProtoAxis.contains("autorange"));

Acts::ProtoAxis vpabRead = Acts::ProtoAxisJsonConverter::fromJson(jProtoAxis);

BOOST_CHECK_EQUAL(vpabRead.getAxisDirection(), vpab.getAxisDirection());
BOOST_CHECK_EQUAL(vpabRead.getAxis(), vpab.getAxis());
BOOST_CHECK_EQUAL(vpabRead.isAutorange(), vpab.isAutorange());
BOOST_CHECK_EQUAL(vpabRead.toString(), vpab.toString());
}

BOOST_AUTO_TEST_CASE(InvalidAndValidInputJson) {
// valid eq axis input
nlohmann::json jValidEqAxis = {{"bins", 10},
{"boundary_type", "Bound"},
{"range", std::array<double, 2>{0.0, 1.0}},
{"type", "Equidistant"}};

// Valid input first
nlohmann::json jValidEq = {
{"axis", jValidEqAxis}, {"axis_dir", "AxisX"}, {"autorange", false}};

BOOST_CHECK_NO_THROW(Acts::ProtoAxisJsonConverter::fromJson(jValidEq));

// Invalid input - zero bins
nlohmann::json jInvalidEqAxis = jValidEqAxis;
jInvalidEqAxis["bins"] = 0;

nlohmann::json jInvalidEq = {
{"axis", jInvalidEqAxis}, {"axis_dir", "AxisX"}, {"autorange", false}};

BOOST_CHECK_THROW(Acts::ProtoAxisJsonConverter::fromJson(jInvalidEq),
std::invalid_argument);

// Invalid input - auto range without bins
jInvalidEq = {
{"axis", jInvalidEqAxis}, {"axis_dir", "AxisX"}, {"autorange", true}};
BOOST_CHECK_THROW(Acts::ProtoAxisJsonConverter::fromJson(jInvalidEq),
std::invalid_argument);

// Invalid input - min >= max
jInvalidEqAxis = jValidEqAxis;
jInvalidEqAxis["range"] = std::array<double, 2>{1.0, 0.0};

jInvalidEq = {
{"axis", jInvalidEqAxis}, {"axis_dir", "AxisX"}, {"autorange", false}};

BOOST_CHECK_THROW(Acts::ProtoAxisJsonConverter::fromJson(jInvalidEq),
std::invalid_argument);

nlohmann::json jValidVarAxis = {
{"boundary_type", "Bound"},
{"boundaries", std::vector<double>{0.0, 0.25, 0.75, 1.0}},
{"type", "Variable"}};

// Valid input first
nlohmann::json jValidVar = {
{"axis", jValidVarAxis}, {"axis_dir", "AxisX"}, {"autorange", false}};
BOOST_CHECK_NO_THROW(Acts::ProtoAxisJsonConverter::fromJson(jValidVar));

// Invalid input - less than two edges
nlohmann::json jInvalidVarAxis = jValidVarAxis;
jInvalidVarAxis["boundaries"] = std::vector<double>{0.0};

nlohmann::json jInvalidVar = {
{"axis", jInvalidVarAxis}, {"axis_dir", "AxisX"}, {"autorange", false}};
BOOST_CHECK_THROW(Acts::ProtoAxisJsonConverter::fromJson(jInvalidVar),
std::invalid_argument);

// Invalid input - non-increasing edges
jInvalidVarAxis = jValidVarAxis;
jInvalidVarAxis["boundaries"] = std::vector<double>{0.0, 0.75, 0.25, 1.0};

jInvalidVar = {
{"axis", jInvalidVarAxis}, {"axis_dir", "AxisX"}, {"autorange", false}};

BOOST_CHECK_THROW(Acts::ProtoAxisJsonConverter::fromJson(jInvalidVar),
std::invalid_argument);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 429e34d

Please sign in to comment.