Skip to content

Commit

Permalink
feat: adding json writing, reading infrastructure (#2283)
Browse files Browse the repository at this point in the history
This PR adds the reading/writing infrastructure of the new
`Experimental` detector feature to and from json.

At the same time it streamlines how we use the `json::nlohmann` module:

- As we need polymorphism, the intrinsic `to_json` and `from_json`
nomenclature that would allow auto-translation is practically unusable
for the `Detector`. Hence it is replaced by a consistent
`ObjectJsonConverter::toJson` and `ObjectJsonConverter::fromJson` naming
scheme.
- It changes enum types from hand-written string writing to the
`nlohmann::enum` handling macro consistently
- It introduces nested `Options` struct for future refinement of json
writing
- It adds a dedicated `detray` writing mode for conversion into detray
formal

---------

Co-authored-by: Joana Niermann <53186085+niermann999@users.noreply.github.com>
  • Loading branch information
asalzburger and niermann999 committed Jul 17, 2023
1 parent 8833e70 commit bb68534
Show file tree
Hide file tree
Showing 35 changed files with 2,926 additions and 217 deletions.
28 changes: 28 additions & 0 deletions Core/include/Acts/Detector/detail/PortalHelper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Definitions/Common.hpp"
#include "Acts/Definitions/Direction.hpp"
#include "Acts/Detector/Portal.hpp"
#include "Acts/Geometry/GeometryContext.hpp"
#include "Acts/Utilities/BinningType.hpp"
Expand Down Expand Up @@ -37,6 +38,33 @@ using PortalReplacement =
namespace detail {
namespace PortalHelper {

/// @brief Method to attach a single detector volume to a portal
///
/// @param portal is the portal where the detector volume is going to be attached
/// @param volume is the volume that is attached to the portal
/// @param direction is the direction to which it is attached
///
void attachDetectorVolumeUpdator(Portal& portal,
const std::shared_ptr<DetectorVolume>& volume,
const Direction& direction);

/// @brief Create and attach the multi link updator, the portal will get
/// a volume updator attached, that points to the different sub volumes
/// depending on the global position and binning - single assignment case
///
/// @param gctx the geometry context
/// @param portal is the portal where the detector volume is going to be attached
/// @param volumes are the volumes that are pointed to
/// @param direction is the direction to which it is attached
/// @param boundaries are the value boundaries
/// @param binning is the binning type
///
void attachDetectorVolumesUpdator(
const GeometryContext& gctx, Portal& portal,
const std::vector<std::shared_ptr<DetectorVolume>>& volumes,
const Direction& direction, const std::vector<ActsScalar>& boundaries,
const BinningValue& binning);

/// @brief Create and attach the multi link updator, the portal will get
/// a volume updator attached, that points to the different sub volumes
/// depending on the global position and binning
Expand Down
6 changes: 3 additions & 3 deletions Core/include/Acts/Navigation/NavigationStateUpdators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ class IndexedUpdatorImpl : public INavigationDelegate {
/// These are the cast parameters - copied from constructor
std::array<BinningValue, grid_type::DIM> casts{};

/// A transform to be applied to the position
Transform3 transform = Transform3::Identity();

/// @brief Constructor for a grid based surface attacher
/// @param igrid the grid that is moved into this attacher
/// @param icasts is the cast values array
Expand Down Expand Up @@ -135,9 +138,6 @@ class IndexedUpdatorImpl : public INavigationDelegate {
}

private:
/// A transform to be applied to the position
Transform3 transform = Transform3::Identity();

/// Unroll the cast loop
/// @param position is the position of the update call
/// @param a is the array to be filled
Expand Down
32 changes: 32 additions & 0 deletions Core/src/Detector/detail/PortalHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,38 @@
#include <stdexcept>
#include <utility>

void Acts::Experimental::detail::PortalHelper::attachDetectorVolumeUpdator(
Portal& portal, const std::shared_ptr<DetectorVolume>& volume,
const Direction& direction) {
// Create a shared link instance & delegate
auto volumeLinkImpl =
std::make_unique<const Acts::Experimental::SingleDetectorVolumeImpl>(
volume.get());
Acts::Experimental::DetectorVolumeUpdator volumeLink;
volumeLink.connect<&Acts::Experimental::SingleDetectorVolumeImpl::update>(
std::move(volumeLinkImpl));
// Update the volume link and the store
portal.assignDetectorVolumeUpdator(direction, std::move(volumeLink),
{volume});
}

void Acts::Experimental::detail::PortalHelper::attachDetectorVolumesUpdator(
const GeometryContext& gctx, Portal& portal,
const std::vector<std::shared_ptr<DetectorVolume>>& volumes,
const Direction& direction, const std::vector<ActsScalar>& boundaries,
const BinningValue& binning) {
// Check if the boundaries need a transform
const auto pTransform = portal.surface().transform(gctx);
// Creating a link to the mother
auto volumes1D = std::make_unique<const BoundVolumesGrid1Impl>(
boundaries, binning, unpack_shared_const_vector(volumes),
pTransform.inverse());
DetectorVolumeUpdator dVolumeUpdator;
dVolumeUpdator.connect<&BoundVolumesGrid1Impl::update>(std::move(volumes1D));
portal.assignDetectorVolumeUpdator(direction, std::move(dVolumeUpdator),
volumes);
}

void Acts::Experimental::detail::PortalHelper::attachDetectorVolumeUpdators(
const GeometryContext& gctx,
const std::vector<std::shared_ptr<DetectorVolume>>& volumes,
Expand Down
2 changes: 1 addition & 1 deletion Examples/Io/Json/src/JsonSurfacesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ JsonSurfacesReader::read(const JsonSurfacesReader::Options& options) {
// Decode the surface identifier
Acts::GeometryIdentifier geoId =
GeometryIdHelper::decodeIdentifier(jSurface);
auto surface = Acts::surfaceFromJson(jSurface["value"]);
auto surface = Acts::SurfaceJsonConverter::fromJson(jSurface["value"]);
surfaceElements.emplace_back(geoId, surface);
}
return SurfaceHierachyMap(std::move(surfaceElements));
Expand Down
53 changes: 53 additions & 0 deletions Examples/Python/src/Json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "Acts/Definitions/Algebra.hpp"
#include "Acts/Detector/Detector.hpp"
#include "Acts/Detector/ProtoDetector.hpp"
#include "Acts/Plugins/Json/DetectorJsonConverter.hpp"
#include "Acts/Plugins/Json/JsonMaterialDecorator.hpp"
#include "Acts/Plugins/Json/MaterialMapJsonConverter.hpp"
#include "Acts/Plugins/Json/ProtoDetectorJsonConverter.hpp"
Expand Down Expand Up @@ -163,5 +165,56 @@ void addJson(Context& ctx) {

mex.def("readSurfaceFromJson", ActsExamples::JsonSurfacesReader::read);
}

{
mex.def("writeDetectorToJson",
[](const Acts::GeometryContext& gctx,
const Acts::Experimental::Detector& detector) -> void {
auto jDetector =
Acts::DetectorJsonConverter::toJson(gctx, detector);
std::ofstream out;
out.open(detector.name() + ".json");
out << jDetector.dump(4);
out.close();
});
}

{
mex.def("writeDetectorToJsonDetray",
[](const Acts::GeometryContext& gctx,
const Acts::Experimental::Detector& detector) -> void {
// Detray format test - manipulate for detray
Acts::DetectorVolumeJsonConverter::Options detrayOptions;
detrayOptions.transformOptions.writeIdentity = true;
detrayOptions.transformOptions.transpose = true;
detrayOptions.surfaceOptions.transformOptions =
detrayOptions.transformOptions;
detrayOptions.portalOptions.surfaceOptions =
detrayOptions.surfaceOptions;

auto jDetector = Acts::DetectorJsonConverter::toJsonDetray(
gctx, detector,
Acts::DetectorJsonConverter::Options{detrayOptions});
std::ofstream out;
out.open(detector.name() + "_detray.json");
out << jDetector.dump(4);
out.close();
});
}

{
mex.def(
"readDetectorFromJson",
[](const Acts::GeometryContext& gctx,
const std::string& fileName) -> auto{
auto in = std::ifstream(fileName,
std::ifstream::in | std::ifstream::binary);
nlohmann::json jDetectorIn;
in >> jDetectorIn;
in.close();

return Acts::DetectorJsonConverter::fromJson(gctx, jDetectorIn);
});
}
}
} // namespace Acts::Python
5 changes: 5 additions & 0 deletions Plugins/Json/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ include(ActsTargetLinkLibrariesSystem)
add_library(
ActsPluginJson SHARED
src/AlgebraJsonConverter.cpp
src/DetectorJsonConverter.cpp
src/DetectorVolumeJsonConverter.cpp
src/ExtentJsonConverter.cpp
src/GridJsonConverter.cpp
src/IndexedSurfacesJsonConverter.cpp
src/MaterialMapJsonConverter.cpp
src/MaterialJsonConverter.cpp
src/PortalJsonConverter.cpp
src/ProtoDetectorJsonConverter.cpp
src/SurfaceBoundsJsonConverter.cpp
src/SurfaceJsonConverter.cpp
Expand Down
28 changes: 27 additions & 1 deletion Plugins/Json/include/Acts/Plugins/Json/AlgebraJsonConverter.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of the Acts project.
//
// Copyright (C) 2021 CERN for the benefit of the Acts project
// Copyright (C) 2021-2023 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
Expand All @@ -21,4 +21,30 @@ void to_json(nlohmann::json& j, const Transform3& t);

void from_json(const nlohmann::json& j, Transform3& t);

namespace Transform3JsonConverter {

/// @brief The options for the transform converter
struct Options {
/// Write the identity transform explicitly
bool writeIdentity = false;
/// Apply a transpose to flip column/row order
bool transpose = true;
};

/// @brief The Transform converter to json
///
/// @param t the transform to be converted
/// @param options transformation options
///
/// @return a json object representing the transform
nlohmann::json toJson(const Transform3& t, const Options& options = {});

/// @brief The Transform converter from json
///
/// @param jTransform the transform json transformation
///
/// @return a transform object
Transform3 fromJson(const nlohmann::json& jTransform);

} // namespace Transform3JsonConverter
} // namespace Acts
64 changes: 64 additions & 0 deletions Plugins/Json/include/Acts/Plugins/Json/DetectorJsonConverter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 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/ActsJson.hpp"
#include "Acts/Plugins/Json/DetectorVolumeJsonConverter.hpp"

// Custom Json encoder/decoders

namespace Acts {

namespace Experimental {
class Detector;
}

namespace DetectorJsonConverter {

struct Options {
DetectorVolumeJsonConverter::Options volumeOptions =
DetectorVolumeJsonConverter::Options{};
};

/// @brief Convert to json format
///
/// @param gctx the geometry context
/// @param detector the detector instance
/// @param options the writing options that propagate
/// to the downstream converters
///
/// @return a json object
nlohmann::json toJson(const GeometryContext& gctx,
const Experimental::Detector& detector,
const Options& options = Options{});

/// @brief Convert to detray json format
///
/// @param gctx the geometry context
/// @param detector the detector instance
/// @param options the writing options that propagate
/// to the downstream converters
///
/// @return a json object in detray format
nlohmann::json toJsonDetray(const GeometryContext& gctx,
const Experimental::Detector& detector,
const Options& options = Options{});

/// @brief convert from json format
///
/// @param gctx the geometry context
/// @param jDetector the json object
///
/// @return a newly created shared Detector object
std::shared_ptr<Experimental::Detector> fromJson(
const GeometryContext& gctx, const nlohmann::json& jDetector);

} // namespace DetectorJsonConverter
} // namespace Acts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// This file is part of the Acts project.
//
// Copyright (C) 2023 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/ActsJson.hpp"
#include "Acts/Plugins/Json/AlgebraJsonConverter.hpp"
#include "Acts/Plugins/Json/PortalJsonConverter.hpp"
#include "Acts/Plugins/Json/SurfaceJsonConverter.hpp"

// Custom Json encoder/decoders

namespace Acts {

namespace Experimental {
class DetectorVolume;
class Portal;
} // namespace Experimental

namespace DetectorVolumeJsonConverter {

struct Options {
// The options how surfaces are written out
SurfaceJsonConverter::Options surfaceOptions;
// The options how portals are written out
PortalJsonConverter::Options portalOptions;
// The options how transforms are written out
Transform3JsonConverter::Options transformOptions;
};

/// @brief Convert to json format
///
/// @param gctx the geometry context
/// @param volume the detector volume instance
/// @param detectorVolumes the list of other detector volumes
/// @param portals the list of portals for saving the portal links
/// @param options the options for the conversion
///
/// @return a json object representing the detector volume
nlohmann::json toJson(
const GeometryContext& gctx, const Experimental::DetectorVolume& volume,
const std::vector<const Experimental::DetectorVolume*>& detectorVolumes,
const std::vector<const Experimental::Portal*>& portals = {},
const Options& options = Options{});

/// @brief Convert to json detray format
///
/// @param gctx the geometry context
/// @param volume the detector volume instance
/// @param detectorVolumes the list of other detector volumes
/// @param options the options for the conversion
///
/// @return a json object representing the detector volume
nlohmann::json toJsonDetray(
const GeometryContext& gctx, const Experimental::DetectorVolume& volume,
const std::vector<const Experimental::DetectorVolume*>& detectorVolumes,
const Options& options = Options{});

/// @brief convert from json format
///
/// @param gctx the geometry context
/// @param jVolume the json object representing the detector volume
///
/// @note this only creates a volume in a stand-alone context, not in a detector
/// context. For the latter, use the DetectorJsonConverter that will patch
/// the portals accordingly
std::shared_ptr<Experimental::DetectorVolume> fromJson(
const GeometryContext& gctx, const nlohmann::json& jVolume);

} // namespace DetectorVolumeJsonConverter
} // namespace Acts

0 comments on commit bb68534

Please sign in to comment.