Skip to content

Commit

Permalink
feat: Introduce Json Converter for Surfaces (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
asalzburger committed Jan 22, 2021
1 parent 3ddff2e commit 4bd3a88
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 1 deletion.
1 change: 1 addition & 0 deletions Plugins/Json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ add_library(
src/JsonGeometryConverter.cpp
src/AlgebraJsonConverter.cpp
src/SurfaceBoundsJsonConverter.cpp
src/SurfaceJsonConverter.cpp
src/UtilitiesJsonConverter.cpp)
target_include_directories(
ActsPluginJson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Acts {

static std::vector<std::string> boundTypes = {
"ConeBounds", "CylinderBounds", "DiamondBounds",
"DiscBounds", "EllipseBounds", "LineBounds",
"RadialBounds", "EllipseBounds", "LineBounds",
"RectangleBounds", "TrapezoidBounds", "TriangleBounds",
"DiscTrapezoidBounds", "ConvexPolygonBounds", "AnnulusBounds",
"OtherBounds"};
Expand Down
67 changes: 67 additions & 0 deletions Plugins/Json/include/Acts/Plugins/Json/SurfaceJsonConverter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// This file is part of the Acts project.
//
// Copyright (C) 2021 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 http://mozilla.org/MPL/2.0/.

#pragma once

#include "Acts/Geometry/GeometryContext.hpp"
#include "Acts/Plugins/Json/AlgebraJsonConverter.hpp"
#include "Acts/Plugins/Json/SurfaceBoundsJsonConverter.hpp"
#include "Acts/Surfaces/Surface.hpp"

#include <string>
#include <vector>

#include <nlohmann/json.hpp>

// Custom Json encoder/decoders. Naming is mandated by nlohman::json and thus
// can not match our naming guidelines.
namespace Acts {

static std::vector<std::string> surfaceTypes = {
"ConeSurface", "CylinderSurface", "DiscSurface", "PerigeeSurface",
"PlaneSurface", "StrawSurface", "CurvilinearSurface"};

/// Non-contextual conversion of a surface
///
/// @note it will take the default context
void to_json(nlohmann::json& j, const Surface& surface);

/// Contextual conversion of a surface
///
/// @param j the json to be filled
/// @param surface the surface to be converted
/// @param gctx the geometry context for this
void toJson(nlohmann::json& j, const Surface& surface,
const Acts::GeometryContext& gctx);

/// Converstion to Surface from jsonn
///
/// @param j the read-in json object
///
/// @return a shared_ptr to a surface object for type polymorphism
std::shared_ptr<Surface> surfaceFromJson(const nlohmann::json&);

/// Converstion to Surface from json in correct type
///
/// The type is given as a template argument in order to be able
/// to construct the correct fitting types for surfaces.
///
/// @param j the read-in json object
///
/// @return a shared_ptr to a typed surface object for type polymorphism
template <typename surface_t, typename bounds_t>
std::shared_ptr<surface_t> surfaceFromJsonT(const nlohmann::json& j) {
Transform3 sTransform;
nlohmann::json jtrf = j["transform"];
from_json(jtrf, sTransform);
nlohmann::json jbounds = j["bounds"];
auto sBounds = surfaceBoundsFromJson<bounds_t>(jbounds);
return Surface::makeShared<surface_t>(sTransform, std::move(sBounds));
}

} // namespace Acts
97 changes: 97 additions & 0 deletions Plugins/Json/src/SurfaceJsonConverter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// This file is part of the Acts project.
//
// Copyright (C) 2021 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 http://mozilla.org/MPL/2.0/.

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

#include "Acts/Surfaces/AnnulusBounds.hpp"
#include "Acts/Surfaces/ConeBounds.hpp"
#include "Acts/Surfaces/ConeSurface.hpp"
#include "Acts/Surfaces/CylinderBounds.hpp"
#include "Acts/Surfaces/CylinderSurface.hpp"
#include "Acts/Surfaces/DiamondBounds.hpp"
#include "Acts/Surfaces/DiscSurface.hpp"
#include "Acts/Surfaces/DiscTrapezoidBounds.hpp"
#include "Acts/Surfaces/EllipseBounds.hpp"
#include "Acts/Surfaces/LineBounds.hpp"
#include "Acts/Surfaces/PerigeeSurface.hpp"
#include "Acts/Surfaces/PlaneSurface.hpp"
#include "Acts/Surfaces/RadialBounds.hpp"
#include "Acts/Surfaces/RectangleBounds.hpp"
#include "Acts/Surfaces/StrawSurface.hpp"
#include "Acts/Surfaces/TrapezoidBounds.hpp"

void Acts::to_json(nlohmann::json& j, const Acts::Surface& surface) {
Acts::GeometryContext gctx;
toJson(j, surface, gctx);
}

void Acts::toJson(nlohmann::json& j, const Acts::Surface& surface,
const Acts::GeometryContext& gctx) {
const auto& sBounds = surface.bounds();
const auto sTransform = surface.transform(gctx);
j["bounds"] = nlohmann::json(sBounds);
nlohmann::json trfj;
to_json(trfj, sTransform);
j["transform"] = trfj;
j["type"] = surfaceTypes[surface.type()];
j["geo_id"] = surface.geometryId().value();
// @todo add SurfaceMaterial converter when available
}

std::shared_ptr<Acts::Surface> Acts::surfaceFromJson(const nlohmann::json& j) {
std::string sType = j["type"];
std::string bType = j["bounds"]["type"];

std::shared_ptr<Acts::Surface> mutableSf = nullptr;

/// Unroll the types
if (sType == "PlaneSurface") {
if (bType == "EllipseBounds") {
mutableSf = surfaceFromJsonT<Acts::PlaneSurface, Acts::EllipseBounds>(j);
} else if (bType == "RectangleBounds") {
mutableSf =
surfaceFromJsonT<Acts::PlaneSurface, Acts::RectangleBounds>(j);
} else if (bType == "TrapezoidBounds") {
mutableSf =
surfaceFromJsonT<Acts::PlaneSurface, Acts::TrapezoidBounds>(j);
}
} else if (sType == "DiscSurface") {
if (bType == "AnnulusBounds") {
mutableSf = surfaceFromJsonT<Acts::DiscSurface, Acts::AnnulusBounds>(j);
} else if (bType == "RadialBounds") {
mutableSf = surfaceFromJsonT<Acts::DiscSurface, Acts::RadialBounds>(j);
} else if (bType == "DiscTrapezoidBounds") {
mutableSf =
surfaceFromJsonT<Acts::DiscSurface, Acts::DiscTrapezoidBounds>(j);
}
} else if (sType == "CylinderSurface") {
mutableSf =
surfaceFromJsonT<Acts::CylinderSurface, Acts::CylinderBounds>(j);
} else if (sType == "ConeSurface") {
mutableSf = surfaceFromJsonT<Acts::ConeSurface, Acts::ConeBounds>(j);
} else if (sType == "StrawSurface") {
mutableSf = surfaceFromJsonT<Acts::StrawSurface, Acts::LineBounds>(j);
} else if (sType == "PerigeeSurface") {
Transform3 pTransform;
nlohmann::json trfj = j["transform"];
;
from_json(trfj, pTransform);
mutableSf = Surface::makeShared<PerigeeSurface>(pTransform);
}

if (mutableSf != nullptr) {
GeometryIdentifier geoID(j["geo_id"]);
mutableSf->assignGeometryId(geoID);
// @todo add material
// if (j.find("material") != j.end() and not j["material"].empty()) {
//
// }
}

return mutableSf;
}
1 change: 1 addition & 0 deletions Tests/UnitTests/Plugins/Json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ add_unittest(MaterialMapJsonConverter MaterialMapJsonConverterTests.cpp)
add_unittest(AlgebraJsonConverter AlgebraJsonConverterTests.cpp)
add_unittest(UtilitiesJsonConverter UtilitiesJsonConverterTests.cpp)
add_unittest(SurfaceBoundsJsonConverter SurfaceBoundsJsonConverterTests.cpp)
add_unittest(SurfaceJsonConverter SurfaceJsonConverterTests.cpp)
204 changes: 204 additions & 0 deletions Tests/UnitTests/Plugins/Json/SurfaceJsonConverterTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
// This file is part of the Acts project.
//
// Copyright (C) 2021 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 http://mozilla.org/MPL/2.0/.

#include <boost/test/unit_test.hpp>

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Geometry/GeometryIdentifier.hpp"
#include "Acts/Plugins/Json/SurfaceJsonConverter.hpp"
#include "Acts/Surfaces/ConeBounds.hpp"
#include "Acts/Surfaces/ConeSurface.hpp"
#include "Acts/Surfaces/CylinderBounds.hpp"
#include "Acts/Surfaces/CylinderSurface.hpp"
#include "Acts/Surfaces/DiscSurface.hpp"
#include "Acts/Surfaces/LineBounds.hpp"
#include "Acts/Surfaces/PerigeeSurface.hpp"
#include "Acts/Surfaces/PlaneSurface.hpp"
#include "Acts/Surfaces/RadialBounds.hpp"
#include "Acts/Surfaces/StrawSurface.hpp"
#include "Acts/Surfaces/TrapezoidBounds.hpp"

#include <fstream>
#include <iostream>

#include <nlohmann/json.hpp>

using namespace Acts;

std::ofstream out;

Acts::GeometryContext gctx;

BOOST_AUTO_TEST_SUITE(SurfaceJsonConverter)

BOOST_AUTO_TEST_CASE(ConeSurfaceRoundTripTests) {
Transform3 trf(Transform3::Identity() * Translation3(0., 0., -7.));
auto cone = std::make_shared<ConeBounds>(0.123, 10., 100.);
auto coneRef = Surface::makeShared<ConeSurface>(trf, cone);
coneRef->assignGeometryId(GeometryIdentifier(13u));

// Test a rectangle
nlohmann::json coneOut;
to_json(coneOut, *coneRef);
out.open("ConeSurface.json");
out << coneOut.dump(2);
out.close();

auto in = std::ifstream("ConeSurface.json",
std::ifstream::in | std::ifstream::binary);
BOOST_CHECK(in.good());
nlohmann::json coneIn;
in >> coneIn;
in.close();

auto coneTest = surfaceFromJson(coneIn);

BOOST_CHECK(coneTest->transform(gctx).isApprox(coneRef->transform(gctx)));
BOOST_CHECK(coneTest->geometryId() == coneRef->geometryId());
BOOST_CHECK(coneTest->bounds() == coneRef->bounds());
}

BOOST_AUTO_TEST_CASE(DiscSurfaceRoundTripTests) {
Transform3 trf(Transform3::Identity() * Translation3(0., 0., -7.));
auto ring = std::make_shared<RadialBounds>(0., 4.);
auto ringDiscRef = Surface::makeShared<DiscSurface>(trf, ring);
ringDiscRef->assignGeometryId(GeometryIdentifier(10u));

// Test a rectangle
nlohmann::json discOut;
to_json(discOut, *ringDiscRef);
out.open("DiscSurface.json");
out << discOut.dump(2);
out.close();

auto in = std::ifstream("DiscSurface.json",
std::ifstream::in | std::ifstream::binary);
BOOST_CHECK(in.good());
nlohmann::json discIn;
in >> discIn;
in.close();

auto ringDiscTest = surfaceFromJson(discIn);

BOOST_CHECK(
ringDiscTest->transform(gctx).isApprox(ringDiscRef->transform(gctx)));
BOOST_CHECK(ringDiscTest->geometryId() == ringDiscRef->geometryId());
BOOST_CHECK(ringDiscTest->bounds() == ringDiscRef->bounds());
}

BOOST_AUTO_TEST_CASE(CylinderSurfaceRoundTripTests) {
Transform3 trf(Transform3::Identity() * Translation3(0., 0., -7.));
auto tube = std::make_shared<CylinderBounds>(5., 20.);
auto cylinderRef = Surface::makeShared<CylinderSurface>(trf, tube);
cylinderRef->assignGeometryId(GeometryIdentifier(11u));

// Test a rectangle
nlohmann::json cylinderOut;
to_json(cylinderOut, *cylinderRef);
out.open("CylinderSurface.json");
out << cylinderOut.dump(2);
out.close();

auto in = std::ifstream("CylinderSurface.json",
std::ifstream::in | std::ifstream::binary);
BOOST_CHECK(in.good());
nlohmann::json cylinderIn;
in >> cylinderIn;
in.close();

auto cylinderTest = surfaceFromJson(cylinderIn);

BOOST_CHECK(
cylinderTest->transform(gctx).isApprox(cylinderRef->transform(gctx)));
BOOST_CHECK(cylinderTest->geometryId() == cylinderRef->geometryId());
BOOST_CHECK(cylinderTest->bounds() == cylinderRef->bounds());
}

BOOST_AUTO_TEST_CASE(PlaneSurfaceRoundTripTests) {
Transform3 trf(Transform3::Identity() * Translation3(0., 0., -7.));
auto trapezoid = std::make_shared<TrapezoidBounds>(2., 3., 4.);
auto trapezoidPlaneRef = Surface::makeShared<PlaneSurface>(trf, trapezoid);
trapezoidPlaneRef->assignGeometryId(GeometryIdentifier(9u));

// Test a rectangle
nlohmann::json planeOut;
to_json(planeOut, *trapezoidPlaneRef);
out.open("PlaneSurface.json");
out << planeOut.dump(2);
out.close();

auto in = std::ifstream("PlaneSurface.json",
std::ifstream::in | std::ifstream::binary);
BOOST_CHECK(in.good());
nlohmann::json planeIn;
in >> planeIn;
in.close();

auto trapezoidPlaneTest = surfaceFromJson(planeIn);

BOOST_CHECK(trapezoidPlaneTest->transform(gctx).isApprox(
trapezoidPlaneRef->transform(gctx)));
BOOST_CHECK(trapezoidPlaneTest->geometryId() ==
trapezoidPlaneRef->geometryId());
BOOST_CHECK(trapezoidPlaneTest->bounds() == trapezoidPlaneRef->bounds());
}

BOOST_AUTO_TEST_CASE(StrawSurfaceRoundTripTests) {
Transform3 trf(Transform3::Identity() * Translation3(0., 0., -7.));
auto straw = std::make_shared<LineBounds>(1., 100.);
auto strawRef = Surface::makeShared<StrawSurface>(trf, straw);
strawRef->assignGeometryId(GeometryIdentifier(12u));

// Test a rectangle
nlohmann::json strawOut;
to_json(strawOut, *strawRef);
out.open("StrawSurface.json");
out << strawOut.dump(2);
out.close();

auto in = std::ifstream("StrawSurface.json",
std::ifstream::in | std::ifstream::binary);
BOOST_CHECK(in.good());
nlohmann::json strawIn;
in >> strawIn;
in.close();

auto strawTest = surfaceFromJson(strawIn);

BOOST_CHECK(strawTest->transform(gctx).isApprox(strawRef->transform(gctx)));
BOOST_CHECK(strawTest->geometryId() == strawRef->geometryId());
BOOST_CHECK(strawTest->bounds() == strawRef->bounds());
}

BOOST_AUTO_TEST_CASE(PerigeeRoundTripTests) {
Transform3 trf(Transform3::Identity() * Translation3(-1., -2., -7.));
auto perigeeRef = Surface::makeShared<PerigeeSurface>(trf);
perigeeRef->assignGeometryId(GeometryIdentifier(99u));

// Test a rectangle
nlohmann::json perigeeOut;
to_json(perigeeOut, *perigeeRef);
out.open("PerigeeSurface.json");
out << perigeeOut.dump(2);
out.close();

auto in = std::ifstream("PerigeeSurface.json",
std::ifstream::in | std::ifstream::binary);
BOOST_CHECK(in.good());
nlohmann::json perigeeIn;
in >> perigeeIn;
in.close();

auto perigeeTest = surfaceFromJson(perigeeIn);

BOOST_CHECK(
perigeeTest->transform(gctx).isApprox(perigeeRef->transform(gctx)));
BOOST_CHECK(perigeeTest->geometryId() == perigeeRef->geometryId());
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 4bd3a88

Please sign in to comment.