Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 07:53:01

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Plugins/Json/MaterialJsonConverter.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Geometry/GeometryContext.hpp"
0013 #include "Acts/Material/BinnedSurfaceMaterial.hpp"
0014 #include "Acts/Material/GridSurfaceMaterial.hpp"
0015 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0016 #include "Acts/Material/HomogeneousVolumeMaterial.hpp"
0017 #include "Acts/Material/ISurfaceMaterial.hpp"
0018 #include "Acts/Material/IVolumeMaterial.hpp"
0019 #include "Acts/Material/InterpolatedMaterialMap.hpp"
0020 #include "Acts/Material/MaterialGridHelper.hpp"
0021 #include "Acts/Material/MaterialSlab.hpp"
0022 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0023 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0024 #include "Acts/Plugins/Json/GeometryJsonKeys.hpp"
0025 #include "Acts/Plugins/Json/GridJsonConverter.hpp"
0026 #include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp"
0027 #include "Acts/Surfaces/Surface.hpp"
0028 #include "Acts/Utilities/BinUtility.hpp"
0029 #include "Acts/Utilities/Grid.hpp"
0030 #include "Acts/Utilities/GridAxisGenerators.hpp"
0031 #include "Acts/Utilities/TypeList.hpp"
0032 
0033 #include <algorithm>
0034 #include <cstddef>
0035 #include <functional>
0036 #include <numbers>
0037 #include <stdexcept>
0038 #include <string>
0039 #include <tuple>
0040 #include <utility>
0041 #include <vector>
0042 
0043 namespace {
0044 
0045 // Grid definition : eq bound
0046 template <typename value_type>
0047 using GridEqBound =
0048     Acts::Grid<value_type, Acts::Axis<Acts::AxisType::Equidistant,
0049                                       Acts::AxisBoundaryType::Bound>>;
0050 // Grid definition : eq closed
0051 template <typename value_type>
0052 using GridEqClosed =
0053     Acts::Grid<value_type, Acts::Axis<Acts::AxisType::Equidistant,
0054                                       Acts::AxisBoundaryType::Closed>>;
0055 
0056 // Grid definition : eq bound eq bound
0057 template <typename value_type>
0058 using GridEqBoundEqBound = Acts::Grid<
0059     value_type,
0060     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>,
0061     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>>;
0062 
0063 // Grid definition : eq bound eq closed
0064 template <typename value_type>
0065 using GridEqBoundEqClosed = Acts::Grid<
0066     value_type,
0067     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>,
0068     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Closed>>;
0069 
0070 // Grid definition : eq closed eq bound
0071 template <typename value_type>
0072 using GridEqClosedEqBound = Acts::Grid<
0073     value_type,
0074     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Closed>,
0075     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>>;
0076 
0077 /// @brief Helper function to convert a grid surface material to json
0078 ///
0079 /// @tparam indexed_grid_materital_t
0080 /// @param jMaterial the json object to written into
0081 /// @param indexedMaterialCandidate the actual indexed material
0082 template <typename indexed_grid_materital_t>
0083 void convertIndexedGridMaterial(
0084     nlohmann::json& jMaterial,
0085     const Acts::ISurfaceMaterial& indexedMaterialCandidate) {
0086   // Check if the material is of the right type
0087   const indexed_grid_materital_t* indexedMaterial =
0088       dynamic_cast<const indexed_grid_materital_t*>(&indexedMaterialCandidate);
0089 
0090   if (indexedMaterial != nullptr) {
0091     // It is a grid type material
0092     jMaterial[Acts::jsonKey().typekey] = "grid";
0093     nlohmann::json jMaterialAccessor;
0094     // Assume globally indexed first
0095     jMaterialAccessor["type"] = "globally_indexed";
0096 
0097     // If we have a globally indexed map, the material data is loaded elsewhere,
0098     // locally indexed material vectors are written though
0099     const auto& materialAccessor = indexedMaterial->materialAccessor();
0100 
0101     if constexpr (std::is_same_v<decltype(materialAccessor),
0102                                  const Acts::IndexedMaterialAccessor&>) {
0103       // It's actually locally indexed
0104       jMaterialAccessor["type"] = "indexed";
0105 
0106       nlohmann::json jMaterialData;
0107       for (const auto& msl : materialAccessor.material) {
0108         jMaterialData.push_back(msl);
0109       }
0110       jMaterialAccessor["storage_vector"] = jMaterialData;
0111     }
0112     // Write the index grid
0113     jMaterialAccessor["grid"] =
0114         Acts::GridJsonConverter::toJson(indexedMaterial->grid());
0115     jMaterial["accessor"] = jMaterialAccessor;
0116 
0117     // Global and bound -> grid local
0118     jMaterial["global_to_grid_local"] = Acts::GridAccessJsonConverter::toJson(
0119         *(indexedMaterial->globalToGridLocalDelegate().instance()));
0120 
0121     jMaterial["bound_to_grid_local"] = Acts::GridAccessJsonConverter::toJson(
0122         *(indexedMaterial->boundToGridLocalDelegate().instance()));
0123   }
0124 }
0125 
0126 /// @brief Unrolling function for catching the right instance
0127 ///
0128 /// @param jMaterial is the json object to be written into
0129 /// @param indexedMaterial is the indexed material
0130 template <typename... Args>
0131 void unrollIndexedGridConversion(nlohmann::json& jMaterial,
0132                                  const Acts::ISurfaceMaterial& indexedMaterial,
0133                                  Acts::TypeList<Args...> /*unused*/) {
0134   (convertIndexedGridMaterial<Args>(jMaterial, indexedMaterial), ...);
0135 }
0136 
0137 template <typename IndexedAccessorType>
0138 Acts::ISurfaceMaterial* indexedMaterialFromJson(nlohmann::json& jMaterial) {
0139   // Load accessor and grid
0140   nlohmann::json jMaterialAccessor = jMaterial["accessor"];
0141 
0142   // Prepare the material and its accessor
0143   IndexedAccessorType materialAccessor(std::vector<Acts::MaterialSlab>{});
0144 
0145   // If it's locally indexed, we need to load the material vector
0146   if constexpr (std::is_same_v<IndexedAccessorType,
0147                                Acts::IndexedMaterialAccessor>) {
0148     // It's actually locally indexed
0149     for (const auto& msl : jMaterialAccessor["storage_vector"]) {
0150       Acts::MaterialSlab mat = Acts::MaterialSlab::Nothing();
0151       from_json(msl, mat);
0152       materialAccessor.material.push_back(mat);
0153     }
0154   }
0155 
0156   // Now make the grid and the axes
0157   nlohmann::json jGrid = jMaterialAccessor["grid"];
0158   nlohmann::json jGridAxes = jGrid["axes"];
0159 
0160   Acts::AxisBoundaryType boundaryType0 = jGridAxes[0]["boundary_type"];
0161 
0162   // 1-dimensional case
0163   if (jGridAxes.size() == 1u) {
0164     // Bound case
0165     if (boundaryType0 == Acts::AxisBoundaryType::Bound) {
0166       Acts::GridAxisGenerators::EqBound eqBound{jGridAxes[0]["range"],
0167                                                 jGridAxes[0]["bins"]};
0168       auto grid =
0169           Acts::GridJsonConverter::fromJson<decltype(eqBound), std::size_t>(
0170               jGrid, eqBound);
0171 
0172       auto boundToGridLocal =
0173           Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson(
0174               jMaterial["bound_to_grid_local"]);
0175 
0176       auto globalToGridLocal =
0177           Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson(
0178               jMaterial["global_to_grid_local"]);
0179 
0180       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0181           std::move(grid), std::move(materialAccessor),
0182           std::move(boundToGridLocal), std::move(globalToGridLocal));
0183     }
0184     // Closed case
0185     if (boundaryType0 == Acts::AxisBoundaryType::Closed) {
0186       Acts::GridAxisGenerators::EqClosed eqClosed{jGridAxes[0]["range"],
0187                                                   jGridAxes[0]["bins"]};
0188       auto grid =
0189           Acts::GridJsonConverter::fromJson<decltype(eqClosed), std::size_t>(
0190               jGrid, eqClosed);
0191 
0192       auto boundToGridLocal =
0193           Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson(
0194               jMaterial["bound_to_grid_local"]);
0195 
0196       auto globalToGridLocal =
0197           Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson(
0198               jMaterial["global_to_grid_local"]);
0199 
0200       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0201           std::move(grid), std::move(materialAccessor),
0202           std::move(boundToGridLocal), std::move(globalToGridLocal));
0203     }
0204   }
0205 
0206   // 2-dimensional case
0207   if (jGridAxes.size() == 2u) {
0208     // Second boundary type
0209     Acts::AxisBoundaryType boundaryType1 = jGridAxes[1]["boundary_type"];
0210 
0211     // Bound-bound setup
0212     if (boundaryType0 == Acts::AxisBoundaryType::Bound &&
0213         boundaryType1 == Acts::AxisBoundaryType::Bound) {
0214       Acts::GridAxisGenerators::EqBoundEqBound eqBoundEqBound{
0215           jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"],
0216           jGridAxes[1]["bins"]};
0217       auto grid =
0218           Acts::GridJsonConverter::fromJson<decltype(eqBoundEqBound),
0219                                             std::size_t>(jGrid, eqBoundEqBound);
0220 
0221       auto boundToGridLocal =
0222           Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson(
0223               jMaterial["bound_to_grid_local"]);
0224 
0225       auto globalToGridLocal =
0226           Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson(
0227               jMaterial["global_to_grid_local"]);
0228 
0229       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0230           std::move(grid), std::move(materialAccessor),
0231           std::move(boundToGridLocal), std::move(globalToGridLocal));
0232     }
0233 
0234     // Bound-closed setup
0235     if (boundaryType0 == Acts::AxisBoundaryType::Bound &&
0236         boundaryType1 == Acts::AxisBoundaryType::Closed) {
0237       Acts::GridAxisGenerators::EqBoundEqClosed eqBoundEqClosed{
0238           jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"],
0239           jGridAxes[1]["bins"]};
0240       auto grid = Acts::GridJsonConverter::fromJson<decltype(eqBoundEqClosed),
0241                                                     std::size_t>(
0242           jGrid, eqBoundEqClosed);
0243 
0244       auto boundToGridLocal =
0245           Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson(
0246               jMaterial["bound_to_grid_local"]);
0247 
0248       auto globalToGridLocal =
0249           Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson(
0250               jMaterial["global_to_grid_local"]);
0251 
0252       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0253           std::move(grid), std::move(materialAccessor),
0254           std::move(boundToGridLocal), std::move(globalToGridLocal));
0255     }
0256 
0257     // Closed-bound setup
0258     if (boundaryType0 == Acts::AxisBoundaryType::Closed &&
0259         boundaryType1 == Acts::AxisBoundaryType::Bound) {
0260       Acts::GridAxisGenerators::EqClosedEqBound eqClosedEqBound{
0261           jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"],
0262           jGridAxes[1]["bins"]};
0263       auto grid = Acts::GridJsonConverter::fromJson<decltype(eqClosedEqBound),
0264                                                     std::size_t>(
0265           jGrid, eqClosedEqBound);
0266 
0267       auto boundToGridLocal =
0268           Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson(
0269               jMaterial["bound_to_grid_local"]);
0270 
0271       auto globalToGridLocal =
0272           Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson(
0273               jMaterial["global_to_grid_local"]);
0274 
0275       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0276           std::move(grid), std::move(materialAccessor),
0277           std::move(boundToGridLocal), std::move(globalToGridLocal));
0278     }
0279   }
0280 
0281   return nullptr;
0282 }
0283 
0284 }  // namespace
0285 
0286 void Acts::to_json(nlohmann::json& j, const Material& t) {
0287   if (t.isVacuum()) {
0288     return;
0289   }
0290   for (unsigned i = 0; i < t.parameters().size(); ++i) {
0291     j.push_back(t.parameters()[i]);
0292   }
0293 }
0294 
0295 void Acts::from_json(const nlohmann::json& j, Material& t) {
0296   if (j.is_null()) {
0297     return;
0298   }
0299   Acts::Material::ParametersVector params =
0300       Acts::Material::ParametersVector::Zero();
0301   for (auto i = params.size(); 0 < i--;) {
0302     // .at(...) ensures bound checks
0303     params[i] = j.at(i);
0304   }
0305   t = Acts::Material(params);
0306   return;
0307 }
0308 
0309 void Acts::to_json(nlohmann::json& j, const MaterialSlab& t) {
0310   nlohmann::json jmat(t.material());
0311   j["material"] = jmat;
0312   j["thickness"] = t.thickness();
0313 }
0314 
0315 void Acts::from_json(const nlohmann::json& j, MaterialSlab& t) {
0316   Material mat = Material::Vacuum();
0317   from_json(j.at("material"), mat);
0318   t = Acts::MaterialSlab(mat, j.at("thickness").get<float>());
0319 }
0320 
0321 void Acts::from_json(const nlohmann::json& j, MaterialSlabMatrix& t) {
0322   // the input data must be array[array[object]]
0323   for (auto& outer : j) {
0324     Acts::MaterialSlabVector mpVector;
0325     for (auto& inner : outer) {
0326       MaterialSlab mat = MaterialSlab::Nothing();
0327       from_json(inner, mat);
0328       mpVector.emplace_back(mat);
0329     }
0330     t.push_back(std::move(mpVector));
0331   }
0332 }
0333 
0334 void Acts::to_json(nlohmann::json& j, const surfaceMaterialPointer& material) {
0335   nlohmann::json jMaterial;
0336   // A bin utility needs to be written
0337   const Acts::BinUtility* bUtility = nullptr;
0338 
0339   // First: Check if we have a proto material
0340   auto psMaterial = dynamic_cast<const Acts::ProtoSurfaceMaterial*>(material);
0341   if (psMaterial != nullptr) {
0342     // Type is proto material
0343     jMaterial[Acts::jsonKey().typekey] = "proto";
0344     // Set mapping type
0345     nlohmann::json mapType(material->mappingType());
0346     jMaterial[Acts::jsonKey().maptype] = mapType;
0347     // by default the protoMaterial is not used for mapping
0348     jMaterial[Acts::jsonKey().mapkey] = false;
0349     // write the bin utility
0350     bUtility = &(psMaterial->binning());
0351     // Check in the number of bin is different from 1
0352     auto& binningData = bUtility->binningData();
0353     for (std::size_t ibin = 0; ibin < binningData.size(); ++ibin) {
0354       if (binningData[ibin].bins() > 1) {
0355         jMaterial[Acts::jsonKey().mapkey] = true;
0356         break;
0357       }
0358     }
0359     nlohmann::json jBin(*bUtility);
0360     jMaterial[Acts::jsonKey().binkey] = jBin;
0361     j[Acts::jsonKey().materialkey] = jMaterial;
0362     return;
0363   }
0364 
0365   // Second: check if we have a homogeneous material
0366   auto hsMaterial =
0367       dynamic_cast<const Acts::HomogeneousSurfaceMaterial*>(material);
0368   if (hsMaterial != nullptr) {
0369     // type is homogeneous
0370     jMaterial[Acts::jsonKey().typekey] = "homogeneous";
0371     // Set mapping type
0372     nlohmann::json mapType(material->mappingType());
0373     jMaterial[Acts::jsonKey().maptype] = mapType;
0374     // Material has been mapped
0375     jMaterial[Acts::jsonKey().mapkey] = true;
0376     nlohmann::json jmat(hsMaterial->materialSlab(Acts::Vector3(0., 0., 0.)));
0377     jMaterial[Acts::jsonKey().datakey] = nlohmann::json::array({
0378         nlohmann::json::array({
0379             jmat,
0380         }),
0381     });
0382     j[Acts::jsonKey().materialkey] = jMaterial;
0383     return;
0384   }
0385 
0386   // Next option remaining: BinnedSurface material
0387   auto bsMaterial = dynamic_cast<const Acts::BinnedSurfaceMaterial*>(material);
0388   if (bsMaterial != nullptr) {
0389     // type is binned
0390     jMaterial[Acts::jsonKey().typekey] = "binned";
0391     // Set mapping type
0392     nlohmann::json mapType(material->mappingType());
0393     jMaterial[Acts::jsonKey().maptype] = mapType;
0394     // Material has been mapped
0395     jMaterial[Acts::jsonKey().mapkey] = true;
0396     bUtility = &(bsMaterial->binUtility());
0397     // convert the data
0398     // get the material matrix
0399     nlohmann::json mmat = nlohmann::json::array();
0400     for (const auto& mpVector : bsMaterial->fullMaterial()) {
0401       nlohmann::json mvec = nlohmann::json::array();
0402       for (const auto& mp : mpVector) {
0403         nlohmann::json jmat(mp);
0404         mvec.push_back(jmat);
0405       }
0406       mmat.push_back(std::move(mvec));
0407     }
0408     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0409     // write the bin utility
0410     nlohmann::json jBin(*bUtility);
0411     jMaterial[Acts::jsonKey().binkey] = jBin;
0412     j[Acts::jsonKey().materialkey] = jMaterial;
0413     return;
0414   }
0415 
0416   // Possible indexed grid types
0417   using IndexedSurfaceGrids = Acts::TypeList<
0418       Acts::IndexedSurfaceMaterial<GridEqBound<std::size_t>>,
0419       Acts::IndexedSurfaceMaterial<GridEqClosed<std::size_t>>,
0420       Acts::IndexedSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0421       Acts::IndexedSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0422       Acts::IndexedSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0423 
0424   unrollIndexedGridConversion(jMaterial, *material, IndexedSurfaceGrids{});
0425   if (!jMaterial.empty()) {
0426     j[Acts::jsonKey().materialkey] = jMaterial;
0427     return;
0428   }
0429 
0430   // Possible: globally indexed grid types
0431   using GloballyIndexedSurfaceGrids = Acts::TypeList<
0432       Acts::GloballyIndexedSurfaceMaterial<GridEqBound<std::size_t>>,
0433       Acts::GloballyIndexedSurfaceMaterial<GridEqClosed<std::size_t>>,
0434       Acts::GloballyIndexedSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0435       Acts::GloballyIndexedSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0436       Acts::GloballyIndexedSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0437 
0438   unrollIndexedGridConversion(jMaterial, *material,
0439                               GloballyIndexedSurfaceGrids{});
0440   if (!jMaterial.empty()) {
0441     j[Acts::jsonKey().materialkey] = jMaterial;
0442     return;
0443   }
0444 
0445   // Possible: material grid types
0446   // using MaterialSurfaceGrids = Acts::TypeList<
0447   //    Acts::GridSurfaceMaterial<GridEqBound<std::size_t>>,
0448   //    Acts::GridSurfaceMaterial<GridEqClosed<std::size_t>>,
0449   //    Acts::GridSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0450   //    Acts::GridSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0451   //    Acts::GridSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0452 
0453   // No material the json object is left empty.
0454   return;
0455 }
0456 
0457 void Acts::from_json(const nlohmann::json& j,
0458                      surfaceMaterialPointer& material) {
0459   if (j.find(Acts::jsonKey().materialkey) == j.end()) {
0460     return;
0461   }
0462   nlohmann::json jMaterial = j[Acts::jsonKey().materialkey];
0463   // By default no material is return.
0464   material = nullptr;
0465   if (jMaterial[Acts::jsonKey().mapkey] == false) {
0466     return;
0467   }
0468 
0469   // Grid based material maps
0470   if (jMaterial[Acts::jsonKey().typekey] == "grid") {
0471     material =
0472         indexedMaterialFromJson<Acts::IndexedMaterialAccessor>(jMaterial);
0473     return;
0474   }
0475 
0476   // The bin utility and material
0477   Acts::BinUtility bUtility;
0478   Acts::MaterialSlabMatrix mpMatrix;
0479   Acts::MappingType mapType = Acts::MappingType::Default;
0480   for (auto& [key, value] : jMaterial.items()) {
0481     if (key == Acts::jsonKey().binkey && !value.empty()) {
0482       from_json(value, bUtility);
0483     }
0484     if (key == Acts::jsonKey().datakey && !value.empty()) {
0485       from_json(value, mpMatrix);
0486     }
0487     if (key == Acts::jsonKey().maptype && !value.empty()) {
0488       from_json(value, mapType);
0489     }
0490   }
0491   // Return the appropriate typr of material
0492   if (mpMatrix.empty()) {
0493     material = new Acts::ProtoSurfaceMaterial(bUtility, mapType);
0494   } else if (bUtility.bins() == 1) {
0495     material = new Acts::HomogeneousSurfaceMaterial(mpMatrix[0][0], mapType);
0496   } else {
0497     material = new Acts::BinnedSurfaceMaterial(bUtility, mpMatrix, mapType);
0498   }
0499 }
0500 
0501 void Acts::to_json(nlohmann::json& j, const volumeMaterialPointer& material) {
0502   nlohmann::json jMaterial;
0503   // A bin utility needs to be written
0504   const Acts::BinUtility* bUtility = nullptr;
0505   // Check if we have a proto material
0506   auto pvMaterial = dynamic_cast<const Acts::ProtoVolumeMaterial*>(material);
0507   if (pvMaterial != nullptr) {
0508     // Type is proto material
0509     jMaterial[Acts::jsonKey().typekey] = "proto";
0510     // By default the protoMaterial is not used for mapping
0511     jMaterial[Acts::jsonKey().mapkey] = false;
0512     bUtility = &(pvMaterial->binUtility());
0513     // Check in the number of bin is different from 1
0514     auto& binningData = bUtility->binningData();
0515     for (std::size_t ibin = 0; ibin < binningData.size(); ++ibin) {
0516       if (binningData[ibin].bins() > 1) {
0517         jMaterial[Acts::jsonKey().mapkey] = true;
0518         break;
0519       }
0520     }
0521     // Write the bin utility
0522     nlohmann::json jBin(*bUtility);
0523     jMaterial[Acts::jsonKey().binkey] = jBin;
0524     j[Acts::jsonKey().materialkey] = jMaterial;
0525     return;
0526   }
0527   // Now check if we have a homogeneous material
0528   auto hvMaterial =
0529       dynamic_cast<const Acts::HomogeneousVolumeMaterial*>(material);
0530   if (hvMaterial != nullptr) {
0531     // type is homogeneous
0532     jMaterial[Acts::jsonKey().typekey] = "homogeneous";
0533     jMaterial[Acts::jsonKey().mapkey] = true;
0534     // array of encoded materials w/ one entry
0535     nlohmann::json jmat(hvMaterial->material({0, 0, 0}));
0536     jMaterial[Acts::jsonKey().datakey] = nlohmann::json::array({
0537         jmat,
0538     });
0539     j[Acts::jsonKey().materialkey] = jMaterial;
0540     return;
0541   }
0542   // Only option remaining: material map
0543   auto bvMaterial2D = dynamic_cast<const Acts::InterpolatedMaterialMap<
0544       Acts::MaterialMapper<Acts::MaterialGrid2D>>*>(material);
0545   // Now check if we have a 2D map
0546   if (bvMaterial2D != nullptr) {
0547     // type is binned
0548     jMaterial[Acts::jsonKey().typekey] = "interpolated2D";
0549     jMaterial[Acts::jsonKey().mapkey] = true;
0550     bUtility = &(bvMaterial2D->binUtility());
0551     // convert the data
0552     nlohmann::json mmat = nlohmann::json::array();
0553     Acts::MaterialGrid2D grid = bvMaterial2D->getMapper().getGrid();
0554     for (std::size_t bin = 0; bin < grid.size(); bin++) {
0555       nlohmann::json jmat(Material(grid.at(bin)));
0556       mmat.push_back(jmat);
0557     }
0558     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0559     // Write the bin utility
0560     nlohmann::json jBin(*bUtility);
0561     jMaterial[Acts::jsonKey().binkey] = jBin;
0562     j[Acts::jsonKey().materialkey] = jMaterial;
0563     return;
0564   }
0565   // Only option remaining: material map
0566   auto bvMaterial3D = dynamic_cast<const Acts::InterpolatedMaterialMap<
0567       Acts::MaterialMapper<Acts::MaterialGrid3D>>*>(material);
0568   // Now check if we have a 3D map
0569   if (bvMaterial3D != nullptr) {
0570     // type is binned
0571     jMaterial[Acts::jsonKey().typekey] = "interpolated3D";
0572     jMaterial[Acts::jsonKey().mapkey] = true;
0573     bUtility = &(bvMaterial3D->binUtility());
0574     // convert the data
0575     nlohmann::json mmat = nlohmann::json::array();
0576     Acts::MaterialGrid3D grid = bvMaterial3D->getMapper().getGrid();
0577     for (std::size_t bin = 0; bin < grid.size(); bin++) {
0578       nlohmann::json jmat(Material(grid.at(bin)));
0579       mmat.push_back(jmat);
0580     }
0581     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0582     // Write the bin utility
0583     nlohmann::json jBin(*bUtility);
0584     jMaterial[Acts::jsonKey().binkey] = jBin;
0585     j[Acts::jsonKey().materialkey] = jMaterial;
0586     return;
0587   }
0588 }
0589 
0590 void Acts::from_json(const nlohmann::json& j, volumeMaterialPointer& material) {
0591   if (j.find(Acts::jsonKey().materialkey) == j.end()) {
0592     return;
0593   }
0594   nlohmann::json jMaterial = j[Acts::jsonKey().materialkey];
0595   // By default no material is return.
0596   material = nullptr;
0597   if (jMaterial[Acts::jsonKey().mapkey] == false) {
0598     return;
0599   }
0600   // The bin utility and material
0601   Acts::BinUtility bUtility;
0602   std::vector<Acts::Material> mmat;
0603   for (auto& [key, value] : jMaterial.items()) {
0604     if (key == Acts::jsonKey().binkey && !value.empty()) {
0605       from_json(value, bUtility);
0606     }
0607     if (key == Acts::jsonKey().datakey && !value.empty()) {
0608       for (const auto& bin : value) {
0609         Acts::Material mat = Material::Vacuum();
0610         from_json(bin, mat);
0611         mmat.push_back(mat);
0612       }
0613     }
0614   }
0615   // We have protoMaterial
0616   if (mmat.empty()) {
0617     material = new Acts::ProtoVolumeMaterial(bUtility);
0618     return;
0619   }
0620   if (mmat.size() == 1) {
0621     material = new Acts::HomogeneousVolumeMaterial(mmat[0]);
0622     return;
0623   }
0624   if (bUtility.dimensions() == 2) {
0625     std::function<Acts::Vector2(Acts::Vector3)> transfoGlobalToLocal;
0626     Acts::Grid2D grid = createGrid2D(bUtility, transfoGlobalToLocal);
0627 
0628     Acts::Grid2D::point_t min = grid.minPosition();
0629     Acts::Grid2D::point_t max = grid.maxPosition();
0630     Acts::Grid2D::index_t nBins = grid.numLocalBins();
0631 
0632     Acts::EAxis axis1(min[0], max[0], nBins[0]);
0633     Acts::EAxis axis2(min[1], max[1], nBins[1]);
0634 
0635     // Build the grid and fill it with data
0636     Acts::MaterialGrid2D mGrid(std::make_tuple(axis1, axis2));
0637 
0638     for (std::size_t bin = 0; bin < mmat.size(); bin++) {
0639       mGrid.at(bin) = mmat[bin].parameters();
0640     }
0641     Acts::MaterialMapper<Acts::MaterialGrid2D> matMap(transfoGlobalToLocal,
0642                                                       mGrid);
0643     material = new Acts::InterpolatedMaterialMap<
0644         Acts::MaterialMapper<Acts::MaterialGrid2D>>(std::move(matMap),
0645                                                     bUtility);
0646     return;
0647   }
0648   if (bUtility.dimensions() == 3) {
0649     std::function<Acts::Vector3(Acts::Vector3)> transfoGlobalToLocal;
0650     Acts::Grid3D grid = createGrid3D(bUtility, transfoGlobalToLocal);
0651 
0652     Acts::Grid3D::point_t min = grid.minPosition();
0653     Acts::Grid3D::point_t max = grid.maxPosition();
0654     Acts::Grid3D::index_t nBins = grid.numLocalBins();
0655 
0656     Acts::EAxis axis1(min[0], max[0], nBins[0]);
0657     Acts::EAxis axis2(min[1], max[1], nBins[1]);
0658     Acts::EAxis axis3(min[2], max[2], nBins[2]);
0659 
0660     // Build the grid and fill it with data
0661     Acts::MaterialGrid3D mGrid(std::make_tuple(axis1, axis2, axis3));
0662 
0663     for (std::size_t bin = 0; bin < mmat.size(); bin++) {
0664       mGrid.at(bin) = mmat[bin].parameters();
0665     }
0666     Acts::MaterialMapper<Acts::MaterialGrid3D> matMap(transfoGlobalToLocal,
0667                                                       mGrid);
0668     material = new Acts::InterpolatedMaterialMap<
0669         Acts::MaterialMapper<Acts::MaterialGrid3D>>(std::move(matMap),
0670                                                     bUtility);
0671     return;
0672   }
0673 }
0674 
0675 nlohmann::json Acts::MaterialJsonConverter::toJsonDetray(
0676     const Acts::ISurfaceMaterial& surfaceMaterial, const Acts::Surface& surface,
0677     std::size_t surfaceIndex, std::map<std::size_t, std::size_t>& gridLink) {
0678   nlohmann::json jSurfaceMaterial;
0679 
0680   // Binned material conversion
0681   if (auto binnedMaterial =
0682           dynamic_cast<const BinnedSurfaceMaterial*>(&surfaceMaterial);
0683       binnedMaterial != nullptr) {
0684     // BinUtility modifications
0685     bool swapped = false;
0686     // Get the bin utility (make a copy as we may modify it)
0687     // Detray expects 2-dimensional grid, currently supported are
0688     // x-y, r-phi, phi-z
0689     BinUtility bUtility = binnedMaterial->binUtility();
0690     // Turn the bin value into a 2D grid
0691     if (bUtility.dimensions() == 1u) {
0692       if (bUtility.binningData()[0u].binvalue == AxisDirection::AxisR) {
0693         // Turn to R-Phi
0694         bUtility += BinUtility(1u, -std::numbers::pi, std::numbers::pi, closed,
0695                                AxisDirection::AxisPhi);
0696       } else if (bUtility.binningData()[0u].binvalue == AxisDirection::AxisZ) {
0697         // Turn to Phi-Z - swap needed
0698         BinUtility nbUtility(1u, -std::numbers::pi, std::numbers::pi, closed,
0699                              AxisDirection::AxisPhi);
0700         nbUtility += bUtility;
0701         bUtility = std::move(nbUtility);
0702         swapped = true;
0703       } else {
0704         std::runtime_error("Unsupported binning for Detray");
0705       }
0706     } else if (bUtility.dimensions() == 2u &&
0707                bUtility.binningData()[0u].binvalue == AxisDirection::AxisZ &&
0708                bUtility.binningData()[1u].binvalue == AxisDirection::AxisPhi) {
0709       BinUtility nbUtility(bUtility.binningData()[1u]);
0710       nbUtility += BinUtility{bUtility.binningData()[0u]};
0711       bUtility = std::move(nbUtility);
0712       swapped = true;
0713     }
0714 
0715     AxisDirection bVal0 = bUtility.binningData()[0u].binvalue;
0716     AxisDirection bVal1 = bUtility.binningData()[1u].binvalue;
0717 
0718     // Translate into grid index type
0719     int gridIndexType = 0;
0720     if (bVal0 == AxisDirection::AxisR && bVal1 == AxisDirection::AxisPhi) {
0721       gridIndexType = 0;
0722     } else if (bVal0 == AxisDirection::AxisPhi &&
0723                bVal1 == AxisDirection::AxisZ) {
0724       gridIndexType = 3;
0725     } else if (bVal0 == AxisDirection::AxisX && bVal1 == AxisDirection::AxisY) {
0726       gridIndexType = 2;
0727     } else {
0728       std::runtime_error("Unsupported binning for Detray");
0729     }
0730     // Convert the axes
0731     nlohmann::json jAxes = toJsonDetray(bUtility, surface);
0732     // Create  a grid index, i.e. type, index tuple
0733     nlohmann::json jGridLink;
0734     jGridLink["type"] = gridIndexType;
0735     std::size_t gridIndex = 0;
0736     if (gridLink.contains(gridIndexType)) {
0737       std::size_t& fGridIndex = gridLink[gridIndex];
0738       gridIndex = fGridIndex;
0739       fGridIndex++;
0740     } else {
0741       gridLink[gridIndexType] = 1;
0742     }
0743     jGridLink["index"] = gridIndex;
0744 
0745     // The grid data
0746     jSurfaceMaterial["axes"] = jAxes;
0747     jSurfaceMaterial["grid_link"] = jGridLink;
0748     jSurfaceMaterial["owner_link"] = surfaceIndex;
0749 
0750     // The bins to be filled
0751     nlohmann::json jBins;
0752     auto materialMatrix = binnedMaterial->fullMaterial();
0753     for (std::size_t ib1 = 0; ib1 < materialMatrix.size(); ++ib1) {
0754       for (std::size_t ib0 = 0; ib0 < materialMatrix[0u].size(); ++ib0) {
0755         nlohmann::json jBin;
0756         // Look up the material slab
0757         MaterialSlab slab = materialMatrix[ib1][ib0];
0758         // Translate into a local bin
0759         std::size_t lb0 = swapped ? ib1 : ib0;
0760         std::size_t lb1 = swapped ? ib0 : ib1;
0761         jBin["loc_index"] = std::array<std::size_t, 2u>{lb0, lb1};
0762 
0763         const Material& material = slab.material();
0764         // The content
0765         nlohmann::json jContent;
0766         jContent["thickness"] = slab.thickness();
0767         // The actual material
0768         nlohmann::json jMaterialParams;
0769         if (slab.thickness() > 0.) {
0770           jMaterialParams["params"] =
0771               std::vector<double>{material.X0(),
0772                                   material.L0(),
0773                                   material.Ar(),
0774                                   material.Z(),
0775                                   material.massDensity(),
0776                                   material.molarDensity(),
0777                                   0.};
0778 
0779         } else {
0780           jMaterialParams["params"] =
0781               std::vector<double>{0., 0., 0., 0., 0., 0., 0.};
0782         }
0783         jContent["material"] = jMaterialParams;
0784         jContent["type"] = 6;
0785         jContent["surface_idx"] = surfaceIndex;
0786 
0787         nlohmann::json jContentVector;
0788         jContentVector.push_back(jContent);
0789         jBin["content"] = jContentVector;
0790         jBins.push_back(jBin);
0791       }
0792     }
0793     jSurfaceMaterial["bins"] = jBins;
0794   }
0795   return jSurfaceMaterial;
0796 }
0797 
0798 nlohmann::json Acts::MaterialJsonConverter::toJsonDetray(
0799     const Acts::BinUtility& binUtility, const Surface& surface) {
0800   nlohmann::json jAxes;
0801   for (const auto [ib, bData] : enumerate(binUtility.binningData())) {
0802     nlohmann::json jAxis;
0803     jAxis["bounds"] = bData.option == closed ? 2 : 1;
0804     jAxis["binning"] = 0u;
0805     jAxis["label"] = ib;
0806     jAxis["bins"] = bData.bins();
0807     double offset = 0;
0808     if (bData.binvalue == AxisDirection::AxisZ) {
0809       offset = surface.center(Acts::GeometryContext{}).z();
0810     }
0811     jAxis["edges"] =
0812         std::array<double, 2>{bData.min + offset, bData.max + offset};
0813     jAxes.push_back(jAxis);
0814   }
0815   return jAxes;
0816 }