File indexing completed on 2026-10-09 08:30:24
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsPlugins/Json/SurfaceMaterialJsonConverter.hpp"
0010
0011 #include "Acts/Material/BinnedSurfaceMaterial.hpp"
0012 #include "Acts/Material/GridSurfaceMaterial.hpp"
0013 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0014 #include "Acts/Material/MaterialSlab.hpp"
0015 #include "Acts/Material/MergedMaterialMarker.hpp"
0016 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0017 #include "Acts/Utilities/BinUtility.hpp"
0018 #include "Acts/Utilities/IAxis.hpp"
0019 #include "Acts/Utilities/IMultiAxis.hpp"
0020 #include "ActsPlugins/Json/AxisSpecJsonConverter.hpp"
0021 #include "ActsPlugins/Json/GridJsonConverter.hpp"
0022 #include "ActsPlugins/Json/MaterialJsonConverter.hpp"
0023 #include "ActsPlugins/Json/UtilitiesJsonConverter.hpp"
0024 #include "ActsPlugins/Json/detail/MaterialJsonContext.hpp"
0025
0026 #include <array>
0027 #include <cstddef>
0028 #include <memory>
0029 #include <stdexcept>
0030 #include <string>
0031 #include <type_traits>
0032 #include <utility>
0033 #include <variant>
0034 #include <vector>
0035
0036 namespace {
0037
0038 using namespace Acts;
0039
0040 using EncodeContext = SurfaceMaterialJsonConverter::EncodeContext;
0041 using DecodeContext = SurfaceMaterialJsonConverter::DecodeContext;
0042
0043
0044 constexpr const char* kHomogeneousTag = "homogeneous";
0045 constexpr const char* kBinnedTag = "binned";
0046 constexpr const char* kProtoTag = "proto";
0047 constexpr const char* kProtoGridTag = "proto-grid";
0048 constexpr const char* kMergedMarkerTag = "merged-material-marker";
0049 constexpr const char* kGridTag = "grid";
0050
0051
0052 constexpr const char* kDirectAccessorTag = "direct";
0053 constexpr const char* kIndexedAccessorTag = "indexed";
0054 constexpr const char* kGloballyIndexedAccessorTag = "globally_indexed";
0055
0056 nlohmann::json homogeneousToJson(const HomogeneousSurfaceMaterial& material,
0057 EncodeContext& ) {
0058 nlohmann::json jMaterial;
0059 jMaterial[jsonKey().typekey] = kHomogeneousTag;
0060 jMaterial[jsonKey().maptype] = nlohmann::json(material.mappingType());
0061 jMaterial[jsonKey().mapkey] = true;
0062 nlohmann::json jSlab(material.materialSlab());
0063 jMaterial[jsonKey().datakey] = nlohmann::json::array({
0064 nlohmann::json::array({
0065 jSlab,
0066 }),
0067 });
0068 return jMaterial;
0069 }
0070
0071 nlohmann::json binnedToJson(const BinnedSurfaceMaterial& material,
0072 EncodeContext& ) {
0073 nlohmann::json jMaterial;
0074 jMaterial[jsonKey().typekey] = kBinnedTag;
0075 jMaterial[jsonKey().maptype] = nlohmann::json(material.mappingType());
0076 jMaterial[jsonKey().mapkey] = true;
0077
0078 nlohmann::json jMatrix = nlohmann::json::array();
0079 for (const auto& mpVector : material.fullMaterial()) {
0080 nlohmann::json jVector = nlohmann::json::array();
0081 for (const auto& mp : mpVector) {
0082 jVector.push_back(nlohmann::json(mp));
0083 }
0084 jMatrix.push_back(std::move(jVector));
0085 }
0086 jMaterial[jsonKey().datakey] = std::move(jMatrix);
0087 jMaterial[jsonKey().binkey] = nlohmann::json(material.binUtility());
0088 return jMaterial;
0089 }
0090
0091 nlohmann::json protoToJson(const ProtoSurfaceMaterial& material,
0092 EncodeContext& ) {
0093 nlohmann::json jMaterial;
0094 jMaterial[jsonKey().typekey] = kProtoTag;
0095 jMaterial[jsonKey().maptype] = nlohmann::json(material.mappingType());
0096
0097 jMaterial[jsonKey().mapkey] = false;
0098 const BinUtility& bUtility = material.binning();
0099 for (const auto& bData : bUtility.binningData()) {
0100 if (bData.bins() > 1) {
0101 jMaterial[jsonKey().mapkey] = true;
0102 break;
0103 }
0104 }
0105 if (material.materialKey()) {
0106 jMaterial["material_key"] = *material.materialKey();
0107 }
0108 jMaterial[jsonKey().binkey] = nlohmann::json(bUtility);
0109 return jMaterial;
0110 }
0111
0112 nlohmann::json protoGridToJson(const ProtoGridSurfaceMaterial& material,
0113 EncodeContext& ) {
0114 nlohmann::json jMaterial;
0115 jMaterial[jsonKey().typekey] = kProtoGridTag;
0116 jMaterial[jsonKey().maptype] = nlohmann::json(material.mappingType());
0117 jMaterial[jsonKey().mapkey] = true;
0118 if (material.materialKey()) {
0119 jMaterial["material_key"] = *material.materialKey();
0120 }
0121 jMaterial["axis_specs"] =
0122 MultiAxisSpecJsonConverter::toJson(material.binning());
0123 return jMaterial;
0124 }
0125
0126 nlohmann::json mergedMarkerToJson(const MergedMaterialMarker& material,
0127 EncodeContext& ) {
0128 nlohmann::json jMaterial;
0129 jMaterial[jsonKey().typekey] = kMergedMarkerTag;
0130 if (!material.origins().empty()) {
0131 jMaterial["origins"] = nlohmann::json::array();
0132 for (const auto& origin : material.origins()) {
0133 nlohmann::json entry{{"geometry_id", origin.geometryId.value()}};
0134 if (origin.materialKey) {
0135 entry["material_key"] = *origin.materialKey;
0136 }
0137 jMaterial["origins"].push_back(std::move(entry));
0138 }
0139 }
0140
0141 jMaterial[jsonKey().mapkey] = true;
0142 return jMaterial;
0143 }
0144
0145
0146 nlohmann::json axesToJson(const IMultiAxis2D& multiAxis) {
0147 nlohmann::json jAxes = nlohmann::json::array();
0148 for (std::size_t ia = 0u; ia < multiAxis.getNAxes(); ++ia) {
0149 jAxes.push_back(AxisJsonConverter::toJson(multiAxis.getAxis(ia)));
0150 }
0151 return jAxes;
0152 }
0153
0154
0155
0156 template <typename value_at_t>
0157 nlohmann::json gridDataToJson(const IMultiAxis2D& multiAxis,
0158 value_at_t&& valueAt) {
0159 nlohmann::json jData = nlohmann::json::array();
0160 IMultiAxis2D::LocalBins nBins = multiAxis.getNBins();
0161 for (std::size_t ib0 = 1u; ib0 <= nBins[0u]; ++ib0) {
0162 for (std::size_t ib1 = 1u; ib1 <= nBins[1u]; ++ib1) {
0163 IMultiAxis2D::LocalBins lBin{ib0, ib1};
0164 jData.push_back(nlohmann::json::array(
0165 {std::array<std::size_t, 2u>{ib0, ib1},
0166 valueAt(multiAxis.getGlobalBinFromLocalBins(lBin))}));
0167 }
0168 }
0169 return jData;
0170 }
0171
0172 nlohmann::json slabsToJson(const std::vector<MaterialSlab>& slabs) {
0173 nlohmann::json jSlabs = nlohmann::json::array();
0174 for (const auto& msl : slabs) {
0175 jSlabs.push_back(nlohmann::json(msl));
0176 }
0177 return jSlabs;
0178 }
0179
0180
0181
0182
0183 nlohmann::json gridMaterialToJson(const GridSurfaceMaterial& material,
0184 EncodeContext& ctx) {
0185 nlohmann::json jMaterial;
0186 jMaterial[jsonKey().typekey] = kGridTag;
0187 jMaterial[jsonKey().mapkey] = true;
0188
0189 const IMultiAxis2D& multiAxis = material.multiAxis();
0190 nlohmann::json jGrid;
0191 jGrid["axes"] = axesToJson(multiAxis);
0192
0193 nlohmann::json jAccessor;
0194 std::visit(
0195 [&]<typename storage_t>(const storage_t& storage) {
0196 if constexpr (std::is_same_v<storage_t, GridSurfaceMaterial::Direct>) {
0197 jAccessor["type"] = kDirectAccessorTag;
0198 jGrid["data"] = gridDataToJson(multiAxis, [&](std::size_t bin) {
0199 return nlohmann::json(storage.at(bin));
0200 });
0201 } else {
0202 constexpr bool isLocal =
0203 std::is_same_v<storage_t, GridSurfaceMaterial::Indexed>;
0204 jAccessor["type"] =
0205 isLocal ? kIndexedAccessorTag : kGloballyIndexedAccessorTag;
0206
0207
0208 if constexpr (isLocal) {
0209 jAccessor["storage_vector"] = slabsToJson(storage.material);
0210 } else {
0211 if (storage.material == nullptr) {
0212 throw std::invalid_argument(
0213 "SurfaceMaterialJsonConverter: globally indexed material "
0214 "without a slab store");
0215 }
0216 if (ctx.storeTableEnabled()) {
0217
0218
0219 jAccessor["store"] = ctx.storeId(storage.material);
0220 } else {
0221
0222
0223 jAccessor["storage_vector"] = slabsToJson(*storage.material);
0224 }
0225 }
0226 jGrid["data"] = gridDataToJson(multiAxis, [&](std::size_t bin) {
0227 return nlohmann::json(storage.indices.at(bin));
0228 });
0229 }
0230 },
0231 material.storage());
0232
0233 jAccessor["grid"] = std::move(jGrid);
0234 jMaterial["accessor"] = std::move(jAccessor);
0235 return jMaterial;
0236 }
0237
0238 MappingType readMappingType(const nlohmann::json& jMaterial) {
0239 MappingType mappingType = MappingType::Default;
0240 if (jMaterial.contains(jsonKey().maptype) &&
0241 !jMaterial.at(jsonKey().maptype).is_null()) {
0242 from_json(jMaterial.at(jsonKey().maptype), mappingType);
0243 }
0244 return mappingType;
0245 }
0246
0247 std::unique_ptr<const ISurfaceMaterial> homogeneousFromJson(
0248 const nlohmann::json& jMaterial, const DecodeContext& ) {
0249 MaterialSlabMatrix matrix;
0250 from_json(jMaterial.at(jsonKey().datakey), matrix);
0251 if (matrix.empty() || matrix[0].empty()) {
0252 throw std::invalid_argument(
0253 "SurfaceMaterialJsonConverter: homogeneous material without data");
0254 }
0255 return std::make_unique<const HomogeneousSurfaceMaterial>(
0256 matrix[0][0], 1., readMappingType(jMaterial));
0257 }
0258
0259 std::unique_ptr<const ISurfaceMaterial> binnedFromJson(
0260 const nlohmann::json& jMaterial, const DecodeContext& ) {
0261 BinUtility bUtility;
0262 from_json(jMaterial.at(jsonKey().binkey), bUtility);
0263 MaterialSlabMatrix matrix;
0264 from_json(jMaterial.at(jsonKey().datakey), matrix);
0265 return std::make_unique<const BinnedSurfaceMaterial>(
0266 bUtility, std::move(matrix), 1., readMappingType(jMaterial));
0267 }
0268
0269 std::unique_ptr<const ISurfaceMaterial> protoFromJson(
0270 const nlohmann::json& jMaterial, const DecodeContext& ) {
0271 BinUtility bUtility;
0272 if (jMaterial.contains(jsonKey().binkey) &&
0273 !jMaterial.at(jsonKey().binkey).is_null()) {
0274 from_json(jMaterial.at(jsonKey().binkey), bUtility);
0275 }
0276 return std::make_unique<const ProtoSurfaceMaterial>(
0277 bUtility, readMappingType(jMaterial),
0278 jMaterial.contains("material_key")
0279 ? std::make_optional(jMaterial.at("material_key").get<std::string>())
0280 : std::nullopt);
0281 }
0282
0283 std::unique_ptr<const ISurfaceMaterial> protoGridFromJson(
0284 const nlohmann::json& jMaterial, const DecodeContext& ) {
0285 MultiAxisSpec spec =
0286 MultiAxisSpecJsonConverter::fromJson(jMaterial.at("axis_specs"));
0287 if (spec.size() != 2u) {
0288 throw std::invalid_argument(
0289 "SurfaceMaterialJsonConverter: proto grid material needs exactly two "
0290 "axis specs");
0291 }
0292 MultiAxisSpec2D spec2D{
0293 std::array<AxisSpec, 2u>{spec.axisSpec(0u), spec.axisSpec(1u)}};
0294 return std::make_unique<const ProtoGridSurfaceMaterial>(
0295 spec2D, readMappingType(jMaterial),
0296 jMaterial.contains("material_key")
0297 ? std::make_optional(jMaterial.at("material_key").get<std::string>())
0298 : std::nullopt);
0299 }
0300
0301 std::unique_ptr<const ISurfaceMaterial> mergedMarkerFromJson(
0302 const nlohmann::json& jMaterial, const DecodeContext& ) {
0303 std::vector<MergedMaterialMarker::Origin> origins;
0304 if (jMaterial.contains("origins")) {
0305 for (const auto& entry : jMaterial.at("origins")) {
0306 std::optional<std::string> key;
0307 if (entry.contains("material_key")) {
0308 key = entry.at("material_key").get<std::string>();
0309 if (key->empty()) {
0310 throw std::invalid_argument("Empty material key in merge provenance");
0311 }
0312 }
0313 origins.emplace_back(
0314 GeometryIdentifier(entry.at("geometry_id").get<std::uint64_t>()),
0315 std::move(key));
0316 }
0317 }
0318 return std::make_unique<MergedMaterialMarker>(std::move(origins));
0319 }
0320
0321
0322
0323 template <typename value_t>
0324 std::vector<std::vector<value_t>> gridPayload2D(const nlohmann::json& jGrid,
0325 std::size_t nBins0,
0326 std::size_t nBins1,
0327 const value_t& fill) {
0328 std::vector<std::vector<value_t>> payload(nBins0,
0329 std::vector<value_t>(nBins1, fill));
0330 for (const auto& jEntry : jGrid.at("data")) {
0331 std::array<std::size_t, 2u> lBin = jEntry.at(0u);
0332 if (jEntry.at(1u).is_null() || lBin[0u] < 1u || lBin[0u] > nBins0 ||
0333 lBin[1u] < 1u || lBin[1u] > nBins1) {
0334 continue;
0335 }
0336 payload[lBin[0u] - 1u][lBin[1u] - 1u] =
0337 jEntry.at(1u).template get<value_t>();
0338 }
0339 return payload;
0340 }
0341
0342 std::vector<MaterialSlab> slabsFromJson(const nlohmann::json& jSlabs) {
0343 std::vector<MaterialSlab> slabs;
0344 slabs.reserve(jSlabs.size());
0345 for (const auto& jSlab : jSlabs) {
0346 MaterialSlab slab = MaterialSlab::Nothing();
0347 from_json(jSlab, slab);
0348 slabs.push_back(slab);
0349 }
0350 return slabs;
0351 }
0352
0353 std::unique_ptr<const ISurfaceMaterial> gridFromJson(
0354 const nlohmann::json& jMaterial, const DecodeContext& ctx) {
0355 const nlohmann::json& jAccessor = jMaterial.at("accessor");
0356 const nlohmann::json& jGrid = jAccessor.at("grid");
0357 const nlohmann::json& jAxes = jGrid.at("axes");
0358 std::string accessorType = jAccessor.at("type").get<std::string>();
0359
0360 if (jAxes.size() != 2u) {
0361 throw std::invalid_argument(
0362 "SurfaceMaterialJsonConverter: grid material needs exactly two axes");
0363 }
0364 std::unique_ptr<IAxis> axis0 = AxisJsonConverter::fromJson(jAxes.at(0u));
0365 std::unique_ptr<IAxis> axis1 = AxisJsonConverter::fromJson(jAxes.at(1u));
0366
0367 if (accessorType == kDirectAccessorTag) {
0368 return GridSurfaceMaterial::createDirect(
0369 *axis0, *axis1,
0370 gridPayload2D<MaterialSlab>(jGrid, axis0->getNBins(), axis1->getNBins(),
0371 MaterialSlab::Nothing()));
0372 }
0373
0374 auto indices = gridPayload2D<std::size_t>(jGrid, axis0->getNBins(),
0375 axis1->getNBins(), std::size_t{0u});
0376 if (accessorType == kIndexedAccessorTag) {
0377 return GridSurfaceMaterial::createIndexed(
0378 *axis0, *axis1, slabsFromJson(jAccessor.at("storage_vector")), indices);
0379 }
0380 if (accessorType == kGloballyIndexedAccessorTag) {
0381 detail::MaterialSlabStore store;
0382 if (jAccessor.contains("store")) {
0383
0384
0385 store = ctx.store(jAccessor.at("store").get<std::size_t>());
0386 } else {
0387 store = std::make_shared<std::vector<MaterialSlab>>(
0388 slabsFromJson(jAccessor.at("storage_vector")));
0389 }
0390 return GridSurfaceMaterial::createGloballyIndexed(
0391 *axis0, *axis1, std::move(store), indices);
0392 }
0393 throw std::invalid_argument(
0394 "SurfaceMaterialJsonConverter: unsupported grid material accessor: " +
0395 accessorType);
0396 }
0397
0398 SurfaceMaterialJsonConverter::Config makeDefaultConfig() {
0399 using Config = SurfaceMaterialJsonConverter::Config;
0400 Config cfg;
0401
0402 cfg.encoder.registerFunction(homogeneousToJson);
0403 cfg.encoder.registerFunction(binnedToJson);
0404 cfg.encoder.registerFunction(protoToJson);
0405 cfg.encoder.registerFunction(protoGridToJson);
0406 cfg.encoder.registerFunction(mergedMarkerToJson);
0407
0408
0409 cfg.encoder.registerFunction(gridMaterialToJson);
0410
0411 cfg.decoder.registerKind(kHomogeneousTag, homogeneousFromJson);
0412 cfg.decoder.registerKind(kBinnedTag, binnedFromJson);
0413 cfg.decoder.registerKind(kProtoTag, protoFromJson);
0414 cfg.decoder.registerKind(kProtoGridTag, protoGridFromJson);
0415 cfg.decoder.registerKind(kMergedMarkerTag, mergedMarkerFromJson);
0416 cfg.decoder.registerKind(kGridTag, gridFromJson);
0417
0418 return cfg;
0419 }
0420
0421 }
0422
0423 const Acts::SurfaceMaterialJsonConverter::Config&
0424 Acts::SurfaceMaterialJsonConverter::Config::defaultConfig() {
0425 static const Config cfg = makeDefaultConfig();
0426 return cfg;
0427 }
0428
0429 nlohmann::json Acts::SurfaceMaterialJsonConverter::toJson(
0430 const ISurfaceMaterial& material, const Config& config,
0431 EncodeContext* context) {
0432
0433 EncodeContext inlineContext;
0434 return config.encoder(material,
0435 context != nullptr ? *context : inlineContext);
0436 }
0437
0438 std::unique_ptr<const Acts::ISurfaceMaterial>
0439 Acts::SurfaceMaterialJsonConverter::fromJson(const nlohmann::json& jMaterial,
0440 const Config& config,
0441 const DecodeContext* context) {
0442
0443 if (jMaterial.contains(jsonKey().mapkey) &&
0444 jMaterial.at(jsonKey().mapkey) == false) {
0445 return nullptr;
0446 }
0447
0448 const DecodeContext emptyContext;
0449 return config.decoder(jMaterial,
0450 context != nullptr ? *context : emptyContext);
0451 }