diff --git a/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp b/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp index 26d4e19c45a..5974e32ea27 100644 --- a/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp +++ b/Plugins/Json/include/Acts/Plugins/Json/GridJsonConverter.hpp @@ -9,6 +9,7 @@ #pragma once #include "Acts/Plugins/Json/ActsJson.hpp" +#include "Acts/Utilities/GridAccessHelpers.hpp" #include "Acts/Utilities/IAxis.hpp" #include "Acts/Utilities/detail/AxisFwd.hpp" @@ -35,6 +36,40 @@ nlohmann::json toJsonDetray(const IAxis& ia); } // namespace AxisJsonConverter +namespace GridAccessJsonConverter { + +/// Convert a global to local access to json +/// +/// @param globalToGridLocal the global to grid local access +/// +/// @return a json object to represent global class +nlohmann::json toJson(const GridAccess::IGlobalToGridLocal& globalToGridLocal); + +/// Create a global grid to local instance +/// +/// @param jGlobalToGridLocal the json snippet +/// +/// @return a newly created object +std::unique_ptr globalToGridLocalFromJson( + const nlohmann::json& jGlobalToGridLocal); + +/// Convert a local to local access to json +/// +/// @param boundToGridLocal the local to local access +/// +/// @return a json object to represent local class +nlohmann::json toJson(const GridAccess::IBoundToGridLocal& boundToGridLocal); + +/// Create a local grid to local instance +/// +/// @param jBoundToGridLocal the json snippet +/// +/// @return a newly created object +std::unique_ptr boundToGridLocalFromJson( + const nlohmann::json& jBoundToGridLocal); + +} // namespace GridAccessJsonConverter + namespace GridJsonConverter { /// @brief Templated grid conversion to json diff --git a/Plugins/Json/src/GridJsonConverter.cpp b/Plugins/Json/src/GridJsonConverter.cpp index e500c9de790..19ac9e85bbf 100644 --- a/Plugins/Json/src/GridJsonConverter.cpp +++ b/Plugins/Json/src/GridJsonConverter.cpp @@ -8,6 +8,7 @@ #include "Acts/Plugins/Json/GridJsonConverter.hpp" +#include "Acts/Plugins/Json/AlgebraJsonConverter.hpp" #include "Acts/Utilities/IAxis.hpp" nlohmann::json Acts::AxisJsonConverter::toJson(const IAxis& ia) { @@ -42,3 +43,259 @@ nlohmann::json Acts::AxisJsonConverter::toJsonDetray(const IAxis& ia) { } return jAxis; } + +namespace { + +template +void encodeSubspace( + nlohmann::json& jGlobalToGridLocal, + const Acts::GridAccess::IGlobalToGridLocal& globalToGridLocal, + const Subspace& /*unused*/) { + const Subspace* subspace = dynamic_cast(&globalToGridLocal); + if (subspace != nullptr) { + jGlobalToGridLocal["type"] = "subspace"; + jGlobalToGridLocal["accessors"] = subspace->bValues; + } +} + +template +void encodeTransformedSubspace( + nlohmann::json& jGlobalToGridLocal, + const Acts::GridAccess::IGlobalToGridLocal& globalToGridLocal, + const Subspace& subscpace) { + const Acts::GridAccess::Affine3Transformed* tsubspace = + dynamic_cast*>( + &globalToGridLocal); + if (tsubspace != nullptr) { + encodeSubspace(jGlobalToGridLocal, tsubspace->globalToGridLocal, subscpace); + jGlobalToGridLocal["transform"] = + Acts::Transform3JsonConverter::toJson(tsubspace->transform); + } +} + +template +void encodeSubspaces( + nlohmann::json& jGlobalToGridLocal, + const Acts::GridAccess::IGlobalToGridLocal& globalToGridLocal, + bool transformed, const std::tuple& tAcessors) { + if (transformed) { + std::apply( + [&](auto&&... vals) { + (encodeTransformedSubspace(jGlobalToGridLocal, globalToGridLocal, + vals), + ...); + }, + tAcessors); + } else { + std::apply( + [&](auto&&... vals) { + (encodeSubspace(jGlobalToGridLocal, globalToGridLocal, vals), ...); + }, + tAcessors); + } +} + +template +std::unique_ptr decodeSubspace( + const nlohmann::json& jGlobalToGridLocal) { + std::unique_ptr globalToGridLocal = + nullptr; + if (jGlobalToGridLocal.find("transform") != jGlobalToGridLocal.end()) { + Acts::Transform3 transform = Acts::Transform3JsonConverter::fromJson( + jGlobalToGridLocal.at("transform")); + Acts::GridAccess::GlobalSubspace globalSubspace; + globalToGridLocal = std::make_unique>>(std::move(globalSubspace), + transform); + } else { + globalToGridLocal = + std::make_unique>(); + } + return globalToGridLocal; +} + +} // namespace + +nlohmann::json Acts::GridAccessJsonConverter::toJson( + const GridAccess::IGlobalToGridLocal& globalToGridLocal) { + nlohmann::json jGlobalToGridLocal; + + std::array transformOptions = {false, true}; + + // One dimensional sub spaces + const std::tuple< + GridAccess::GlobalSubspace, GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, GridAccess::GlobalSubspace> + oneDimSubspaces = {}; + + for (bool transform : transformOptions) { + encodeSubspaces(jGlobalToGridLocal, globalToGridLocal, transform, + oneDimSubspaces); + if (!jGlobalToGridLocal.empty()) { + return jGlobalToGridLocal; + } + } + + // Useful two dimensional sub spaces + const std::tuple, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace, + GridAccess::GlobalSubspace> + twoDimSubspaces = {}; + + for (bool transform : transformOptions) { + encodeSubspaces(jGlobalToGridLocal, globalToGridLocal, transform, + twoDimSubspaces); + if (!jGlobalToGridLocal.empty()) { + return jGlobalToGridLocal; + } + } + return jGlobalToGridLocal; +} + +std::unique_ptr +Acts::GridAccessJsonConverter::globalToGridLocalFromJson( + const nlohmann::json& jGlobalToGridLocal) { + std::unique_ptr globalToGridLocal = + nullptr; + + std::vector accessors = + jGlobalToGridLocal.at("accessors").get>(); + + // Switch and fill for 1D + if (accessors.size() == 1u) { + switch (accessors[0]) { + case binX: + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + break; + case binY: + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + break; + case binZ: + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + break; + case binR: + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + break; + case binPhi: + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + break; + case binEta: + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + break; + default: + // globalToGridLocal = nullptr; + break; + } + } + + // Switch and fill for 2D + if (accessors.size() == 2u) { + if (accessors == std::vector{binX, binY}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binY, binX}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binX, binZ}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binZ, binX}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binY, binZ}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binZ, binY}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binR, binPhi}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binPhi, binR}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binZ, binPhi}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } else if (accessors == std::vector{binPhi, binZ}) { + globalToGridLocal = decodeSubspace(jGlobalToGridLocal); + } + // else globalToGridLocal = nullptr; + } + return globalToGridLocal; +} + +nlohmann::json Acts::GridAccessJsonConverter::toJson( + const GridAccess::IBoundToGridLocal& boundToGridLocal) { + nlohmann::json jBoundtoGridLocal; + + auto localSubSpace0 = + dynamic_cast*>(&boundToGridLocal); + if (localSubSpace0 != nullptr) { + jBoundtoGridLocal["type"] = "subspace"; + jBoundtoGridLocal["accessors"] = localSubSpace0->accessors; + } + + auto localSubSpace1 = + dynamic_cast*>(&boundToGridLocal); + if (localSubSpace1 != nullptr) { + jBoundtoGridLocal["type"] = "subspace"; + jBoundtoGridLocal["accessors"] = localSubSpace1->accessors; + } + + auto localSubSpace01 = + dynamic_cast*>(&boundToGridLocal); + if (localSubSpace01 != nullptr) { + jBoundtoGridLocal["type"] = "subspace"; + jBoundtoGridLocal["accessors"] = localSubSpace01->accessors; + } + + auto localSubSpace10 = + dynamic_cast*>(&boundToGridLocal); + if (localSubSpace10 != nullptr) { + jBoundtoGridLocal["type"] = "subspace"; + jBoundtoGridLocal["accessors"] = localSubSpace10->accessors; + } + + auto boundCylinderToZPhi = + dynamic_cast(&boundToGridLocal); + if (boundCylinderToZPhi != nullptr) { + jBoundtoGridLocal["type"] = "cylinder_to_zphi"; + jBoundtoGridLocal["radius"] = boundCylinderToZPhi->radius; + jBoundtoGridLocal["shift"] = boundCylinderToZPhi->shift; + } + + return jBoundtoGridLocal; +} + +std::unique_ptr +Acts::GridAccessJsonConverter::boundToGridLocalFromJson( + const nlohmann::json& jBoundtoGridLocal) { + std::unique_ptr boundToGridLocal = + nullptr; + std::string type = jBoundtoGridLocal.at("type").get(); + if (type == "subspace") { + std::vector accessors = + jBoundtoGridLocal.at("accessors").get>(); + if (accessors.size() == 1 && accessors[0] == 0) { + boundToGridLocal = + std::make_unique>(); + } else if (accessors.size() == 1 && accessors[0] == 1) { + boundToGridLocal = + std::make_unique>(); + } else if (accessors.size() == 2 && accessors[0] == 0 && + accessors[1] == 1) { + boundToGridLocal = + std::make_unique>(); + } else if (accessors.size() == 2 && accessors[0] == 1 && + accessors[1] == 0) { + boundToGridLocal = + std::make_unique>(); + } + } else if (type == "cylinder_to_zphi") { + ActsScalar radius = jBoundtoGridLocal.at("radius").get(); + ActsScalar shift = jBoundtoGridLocal.at("shift").get(); + boundToGridLocal = + std::make_unique(radius, shift); + } + return boundToGridLocal; +} diff --git a/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp b/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp index 3c3b3f24185..ffe4b8ed2e4 100644 --- a/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp +++ b/Tests/UnitTests/Plugins/Json/GridJsonConverterTests.cpp @@ -9,6 +9,8 @@ #include #include "Acts/Plugins/Json/GridJsonConverter.hpp" +#include "Acts/Tests/CommonHelpers/FloatComparisons.hpp" +#include "Acts/Utilities/GridAccessHelpers.hpp" #include "Acts/Utilities/GridAxisGenerators.hpp" #include @@ -249,4 +251,197 @@ BOOST_AUTO_TEST_CASE(Grid2DSingleEntryBoundClosed) { BOOST_CHECK_EQUAL(eqBoundEqClosedJsonRead.atPosition(p33), 33u); } +namespace { +template +bool checkType(const ReferenceType& /**unused*/, + const CheckTypeUniquePtr& g2l) { + return (dynamic_cast(g2l.get()) != nullptr); +} + +template +void checkGlobalSubspaceTuple(const SubspactTuple& sstuple) { + // Test without transform + std::vector jsspace; + std::apply( + [&](auto&&... vals) { + (jsspace.push_back(Acts::GridAccessJsonConverter::toJson(vals)), ...); + }, + sstuple); + + // Test that none of them are empty + for (auto& jss : jsspace) { + BOOST_CHECK(!jss.empty()); + } + + // Read back in + std::vector> sspaceRead; + for (auto& jss : jsspace) { + sspaceRead.push_back( + Acts::GridAccessJsonConverter::globalToGridLocalFromJson(jss)); + } + + // Test that none of them are empty + for (auto& ssp : sspaceRead) { + BOOST_CHECK(ssp != nullptr); + } + + // Check that the type is correct + std::size_t irn = 0; + bool good = true; + std::apply( + [&](auto&&... vals) { + ((good = good && checkType(vals, sspaceRead[irn++])), ...); + }, + sstuple); + BOOST_CHECK(good); + + Acts::Transform3 tTransform; + tTransform.pretranslate(Acts::Vector3{0., 0., 100.}); + + // Test with transform + std::vector jsspaceTransform; + std::apply( + [&](auto... vals) { + (jsspaceTransform.push_back(Acts::GridAccessJsonConverter::toJson( + Acts::GridAccess::Affine3Transformed(vals, + tTransform))), + ...); + }, + sstuple); + + // Test that none of them are empty & everyone has a stransform + for (auto& jss : jsspaceTransform) { + BOOST_CHECK(!jss.empty()); + BOOST_CHECK(jss.find("transform") != jss.end()); + } + + // Read back in + std::vector> + sspaceTransformRead; + for (auto& jss : jsspaceTransform) { + sspaceTransformRead.push_back( + Acts::GridAccessJsonConverter::globalToGridLocalFromJson(jss)); + } + + // Test that none of them are empty + for (auto& ssp : sspaceTransformRead) { + BOOST_CHECK(ssp != nullptr); + } + + // Check that the type is correct + irn = 0; + good = true; + std::apply( + [&](auto... vals) { + ((good = good && + checkType(Acts::GridAccess::Affine3Transformed( + vals, tTransform), + sspaceTransformRead[irn++])), + ...); + }, + sstuple); + BOOST_CHECK(good); +} + +} // namespace + +BOOST_AUTO_TEST_CASE(GlobalSubSpaceTests1D) { + // One dimensional sub spaces + const std::tuple, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace> + sspace1D; + + // Check the tuple for 1D + checkGlobalSubspaceTuple(sspace1D); +} + +BOOST_AUTO_TEST_CASE(GlobalSubSpaceTests2D) { + // Two dimensional sub spaces + const std::tuple, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace, + Acts::GridAccess::GlobalSubspace> + sspace2D = {}; + + // Check the tuple for 2D + checkGlobalSubspaceTuple(sspace2D); +} + +BOOST_AUTO_TEST_CASE(LocalSubspace1D) { + const std::tuple, + Acts::GridAccess::LocalSubspace<1u>, + Acts::GridAccess::LocalSubspace<0u, 1u>, + Acts::GridAccess::LocalSubspace<1u, 0u>> + lspace1D; + + // Write them to json + std::vector jlspace; + std::apply( + [&](auto&&... vals) { + (jlspace.push_back(Acts::GridAccessJsonConverter::toJson(vals)), ...); + }, + lspace1D); + + // Check that none of them is empty + for (auto& jls : jlspace) { + BOOST_CHECK(!jls.empty()); + } + + std::vector> lspaceRead; + for (auto& jls : jlspace) { + lspaceRead.push_back( + Acts::GridAccessJsonConverter::boundToGridLocalFromJson(jls)); + } + + // Test that none of them are empty + for (auto& lsp : lspaceRead) { + BOOST_CHECK(lsp != nullptr); + } + + // Check that the type is correct + std::size_t irn = 0; + bool good = true; + std::apply( + [&](auto&&... vals) { + ((good = good && checkType(vals, lspaceRead[irn++])), ...); + }, + lspace1D); + BOOST_CHECK(good); +} + +BOOST_AUTO_TEST_CASE(BoundCylinderToZPhiTest) { + Acts::GridAccess::BoundCylinderToZPhi boundCylinderToZPhi(100., 10.); + + nlohmann::json jboundCylinderToZPhi = + Acts::GridAccessJsonConverter::toJson(boundCylinderToZPhi); + + // Check it is not empty + BOOST_CHECK(!jboundCylinderToZPhi.empty()); + + auto boundCylinderToZPhiRead = + Acts::GridAccessJsonConverter::boundToGridLocalFromJson( + jboundCylinderToZPhi); + + // Check that it is not empty + BOOST_REQUIRE(boundCylinderToZPhiRead != nullptr); + + const Acts::GridAccess::BoundCylinderToZPhi* bct = + dynamic_cast( + boundCylinderToZPhiRead.get()); + + BOOST_REQUIRE(bct != nullptr); + CHECK_CLOSE_ABS(bct->radius, 100., 1e-5); + CHECK_CLOSE_ABS(bct->shift, 10., 1e-5); +} + BOOST_AUTO_TEST_SUITE_END()