Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions Plugins/Json/include/ActsPlugins/Json/JsonKindDispatcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// 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 <cstddef>
#include <functional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <utility>

#include <nlohmann/json.hpp>

namespace Acts {

/// JSON-specific dispatcher that routes decoder functions based on a string
/// kind tag found in the encoded payload.
template <typename return_t, typename... args_t>
class JsonKindDispatcher {
public:
/// Type of the dispatcher specialization
using self_type = JsonKindDispatcher<return_t, args_t...>;
/// Function signature type
using decoder_signature = return_t(const nlohmann::json&, args_t...);
/// Decoder callable type
using decoder_type = std::function<decoder_signature>;
/// Decoder callable pointer type
using decoder_pointer_type = return_t (*)(args_t...);

/// Explicit constructor of the dispatcher
///
/// @param kindKey the key containing the type kind in json
/// @param context context string for error signaling
explicit JsonKindDispatcher(std::string kindKey = "kind",
std::string context = "JSON payload")
: m_kindKey(std::move(kindKey)), m_context(std::move(context)) {
if (m_kindKey.empty()) {
throw std::invalid_argument(
"JsonKindDispatcher kind key must be non-empty");
}
if (m_context.empty()) {
m_context = "JSON payload";
}
}

/// Register a kind and the corresponding decoder
///
/// @param kind kind to register
/// @param decoder corresponding decoder
///
/// @return reference to this dispatcher instance
self_type& registerKind(std::string kind, decoder_type decoder) {
if (kind.empty()) {
throw std::invalid_argument("JsonKindDispatcher kind must be non-empty");
}
if (!decoder) {
throw std::invalid_argument("JsonKindDispatcher decoder must be valid");
}
auto [_, inserted] =
m_decoders.emplace(std::move(kind), std::move(decoder));
if (!inserted) {
throw std::invalid_argument(
"JsonKindDispatcher duplicate kind registration");
}
return *this;
}

/// Decode the registered kind from a json file
///
/// @param encoded json file to decode
/// @param args forwarding reference to the decoder arguments
///
/// @return the object constructed from the json encoding
template <typename... func_args_t>
return_t operator()(const nlohmann::json& encoded,
func_args_t&&... args) const
requires std::invocable<decoder_pointer_type, func_args_t...>
{
if (!encoded.contains(m_kindKey)) {
throw std::invalid_argument("Missing '" + m_kindKey + "' in " +
m_context);
}

const auto& kindValue = encoded.at(m_kindKey);
if (!kindValue.is_string()) {
throw std::invalid_argument("Invalid '" + m_kindKey + "' type in " +
m_context);
}

const auto kind = kindValue.template get<std::string>();
auto decoder = m_decoders.find(kind);
if (decoder == m_decoders.end()) {
throw std::invalid_argument("Unsupported " + m_context +
" kind: " + kind);
}

if constexpr (std::is_void_v<return_t>) {
decoder->second(encoded, std::forward<func_args_t>(args)...);
return;
} else {
return decoder->second(encoded, std::forward<func_args_t>(args)...);
}
}

/// Check if a certain kind is registered
///
/// @param kind the kind to check for registration
///
/// @return boolean showing registration status
bool hasKind(std::string_view kind) const {
return m_decoders.contains(std::string{kind});
}

/// Clear the registered decoders list
void clear() { m_decoders.clear(); }

/// Get the number of registered decoders
///
/// @return number of registered decoders
std::size_t size() const { return m_decoders.size(); }

private:
std::string m_kindKey;
std::string m_context;
std::unordered_map<std::string, decoder_type> m_decoders;
};

} // namespace Acts
176 changes: 94 additions & 82 deletions Plugins/Json/include/ActsPlugins/Json/SurfaceJsonConverter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,14 @@

#pragma once

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Geometry/GeometryContext.hpp"
#include "Acts/Surfaces/Surface.hpp"
#include "ActsPlugins/Json/ActsJson.hpp"
#include "Acts/Utilities/TypeDispatcher.hpp"
#include "ActsPlugins/Json/AlgebraJsonConverter.hpp"
#include "ActsPlugins/Json/SurfaceBoundsJsonConverter.hpp"
#include "ActsPlugins/Json/JsonKindDispatcher.hpp"

#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include <nlohmann/json.hpp>

namespace Acts {

Expand All @@ -30,9 +24,8 @@ namespace Acts {
class ISurfaceMaterial;

using SurfaceAndMaterialWithContext =
std::tuple<std::shared_ptr<const Acts::Surface>,
std::shared_ptr<const Acts::ISurfaceMaterial>,
Acts::GeometryContext>;
std::tuple<std::shared_ptr<const Surface>,
std::shared_ptr<const ISurfaceMaterial>, GeometryContext>;

/// Convert SurfaceAndMaterialWithContext to JSON
/// @param j Destination JSON object
Expand All @@ -59,78 +52,98 @@ void to_json(nlohmann::json& j, const std::shared_ptr<const Surface>& surface);
void toJson(nlohmann::json& j, const std::shared_ptr<const Surface>& surface,
const Acts::GeometryContext& gctx);

/// Conversion 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& j);

/// Conversion 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) {
nlohmann::json jTransform = j["transform"];
Transform3 sTransform = Transform3JsonConverter::fromJson(jTransform);
if constexpr (std::is_same_v<bounds_t, void>) {
return Surface::makeShared<surface_t>(sTransform);
} else {
nlohmann::json jBounds = j["bounds"];
auto sBounds = SurfaceBoundsJsonConverter::fromJson<bounds_t>(jBounds);
return Surface::makeShared<surface_t>(sTransform, std::move(sBounds));
}
}

namespace SurfaceJsonConverter {

/// Options controlling surface JSON serialization.
struct Options {
/// Transform serialization options
Transform3JsonConverter::Options transformOptions =
Transform3JsonConverter::Options{};
/// Write material information
bool writeMaterial = true;
/// Write surface as portal
bool portal = false;
/// Static class performing JSON conversion of the surfaces
class SurfaceJsonConverter {
public:
/// Options for surface conversion
struct Options {
/// Transform serialization options
Transform3JsonConverter::Options transformOptions =
Transform3JsonConverter::Options{};
/// Write material information
bool writeMaterial = true;
/// Write surface as portal
bool portal = false;
};

/// Encoder type for the surface bounds
using SurfaceBoundsEncoder = TypeDispatcher<SurfaceBounds, nlohmann::json()>;
/// Encoder type for the surfaces
using SurfaceEncoder =
TypeDispatcher<Surface,
nlohmann::json(const GeometryContext&, const Options&)>;

/// Deccoder type for the surfaces
using SurfaceDecoder = JsonKindDispatcher<std::shared_ptr<Surface>>;

/// Configuration struct
struct Config {
/// Encoder for the surfaces
SurfaceEncoder surfaceEncoder{};
/// Encoder for the surface bounds
SurfaceBoundsEncoder surfaceBoundsEncoder{};

/// Decoder for the surfaces
SurfaceDecoder surfaceDecoder{};

/// Default configuration construction
///
/// @return default configuration
static Config defaultConfig();
};

/// Delete the default constructor
/// as the class is purely static (for now)
SurfaceJsonConverter() = delete;

/// Contextual conversion of a surface
///
/// @param gctx the geometry context for this
/// @param surface the surface to be converted
/// @param options the writing options for the surfaces
///
/// @return a json object representing the surface
static nlohmann::json toJson(
const GeometryContext& gctx, const Surface& surface,
const Options& options = Options{
.transformOptions = Transform3JsonConverter::Options{},
.writeMaterial = true,
.portal = false});

/// Contextual conversion of a surface - Detray export
///
/// @param gctx the geometry context for this
/// @param surface the surface to be converted
/// @param options the writing options for the surfaces
///
/// @note reading back detray json is not supported and will fail
///
/// @return a json object representing the surface
static nlohmann::json toJsonDetray(
const GeometryContext& gctx, const Surface& surface,
const Options& options = Options{
.transformOptions = Transform3JsonConverter::Options{},
.writeMaterial = true,
.portal = false});

/// @brief The Surface converter from json
///
/// @param jSurface the surface json object
///
/// @return a shared object created from json input
static std::shared_ptr<Surface> fromJson(const nlohmann::json& jSurface);

/// @brief Set externally constructed configuration
///
/// @note May be removed when the dispatcher migration is finished
///
/// @param cfg configuration to use
static void setConfig(const Config& cfg) { m_cfg = cfg; };

private:
static Config m_cfg;
Comment thread
ssdetlab marked this conversation as resolved.
};

/// Contextual conversion of a surface
///
/// @param gctx the geometry context for this
/// @param surface the surface to be converted
/// @param options the writing options for the surfaces
///
/// @return a json object representing the surface
nlohmann::json toJson(const GeometryContext& gctx, const Surface& surface,
const Options& options = Options{});

/// Contextual conversion of a surface - Detray export
///
/// @param gctx the geometry context for this
/// @param surface the surface to be converted
/// @param options the writing options for the surfaces
///
/// @note reading back detray json is not supported and will fail
///
/// @return a json object representing the surface
nlohmann::json toJsonDetray(const GeometryContext& gctx, const Surface& surface,
const Options& options = Options{});

/// @brief The Surface converter from json
///
/// @param jSurface the surface json object
///
/// @return a shared object created from json input
std::shared_ptr<Surface> fromJson(const nlohmann::json& jSurface);

} // namespace SurfaceJsonConverter

// This macro create a conversion for the surface type
NLOHMANN_JSON_SERIALIZE_ENUM(
Surface::SurfaceType,
Expand All @@ -143,5 +156,4 @@ NLOHMANN_JSON_SERIALIZE_ENUM(
{Surface::SurfaceType::Curvilinear, "CurvilinearSurface"},
{Surface::SurfaceType::Other, "Other"}})

/// @}
} // namespace Acts
Loading
Loading