Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:10:19

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2021 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 http://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Plugins/Json/MaterialJsonConverter.hpp"
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Material/BinnedSurfaceMaterial.hpp"
0013 #include "Acts/Material/GridSurfaceMaterial.hpp"
0014 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0015 #include "Acts/Material/HomogeneousVolumeMaterial.hpp"
0016 #include "Acts/Material/ISurfaceMaterial.hpp"
0017 #include "Acts/Material/IVolumeMaterial.hpp"
0018 #include "Acts/Material/InterpolatedMaterialMap.hpp"
0019 #include "Acts/Material/MaterialGridHelper.hpp"
0020 #include "Acts/Material/ProtoSurfaceMaterial.hpp"
0021 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0022 #include "Acts/Plugins/Json/GeometryJsonKeys.hpp"
0023 #include "Acts/Plugins/Json/GridJsonConverter.hpp"
0024 #include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp"
0025 #include "Acts/Utilities/BinUtility.hpp"
0026 #include "Acts/Utilities/Grid.hpp"
0027 #include "Acts/Utilities/GridAccessHelpers.hpp"
0028 #include "Acts/Utilities/GridAxisGenerators.hpp"
0029 #include "Acts/Utilities/TypeList.hpp"
0030 
0031 #include <algorithm>
0032 #include <cstddef>
0033 #include <functional>
0034 #include <iosfwd>
0035 #include <memory>
0036 #include <stdexcept>
0037 #include <string>
0038 #include <tuple>
0039 #include <utility>
0040 #include <vector>
0041 
0042 namespace {
0043 
0044 // Grid definition : eq bound
0045 template <typename value_type>
0046 using GridEqBound =
0047     Acts::Grid<value_type,
0048                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0049                                   Acts::detail::AxisBoundaryType::Bound>>;
0050 // Grid definition : eq closed
0051 template <typename value_type>
0052 using GridEqClosed =
0053     Acts::Grid<value_type,
0054                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0055                                   Acts::detail::AxisBoundaryType::Closed>>;
0056 
0057 // Grid definition : eq bound eq bound
0058 template <typename value_type>
0059 using GridEqBoundEqBound =
0060     Acts::Grid<value_type,
0061                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0062                                   Acts::detail::AxisBoundaryType::Bound>,
0063                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0064                                   Acts::detail::AxisBoundaryType::Bound>>;
0065 
0066 // Grid definition : eq bound eq closed
0067 template <typename value_type>
0068 using GridEqBoundEqClosed =
0069     Acts::Grid<value_type,
0070                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0071                                   Acts::detail::AxisBoundaryType::Bound>,
0072                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0073                                   Acts::detail::AxisBoundaryType::Closed>>;
0074 
0075 // Grid definition : eq closed eq bound
0076 template <typename value_type>
0077 using GridEqClosedEqBound =
0078     Acts::Grid<value_type,
0079                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0080                                   Acts::detail::AxisBoundaryType::Closed>,
0081                Acts::detail::Axis<Acts::detail::AxisType::Equidistant,
0082                                   Acts::detail::AxisBoundaryType::Bound>>;
0083 
0084 /// @brief Helper function to convert a grid surface material to json
0085 ///
0086 /// @tparam indexed_grid_materital_t
0087 /// @param jMaterial the json object to written into
0088 /// @param indexedMaterialCandidate the actual indexed material
0089 template <typename indexed_grid_materital_t>
0090 void convertIndexedGridMaterial(
0091     nlohmann::json& jMaterial,
0092     const Acts::ISurfaceMaterial& indexedMaterialCandidate) {
0093   // Check if the material is of the right type
0094   const indexed_grid_materital_t* indexedMaterial =
0095       dynamic_cast<const indexed_grid_materital_t*>(&indexedMaterialCandidate);
0096 
0097   if (indexedMaterial != nullptr) {
0098     // It is a grid type material
0099     jMaterial[Acts::jsonKey().typekey] = "grid";
0100     nlohmann::json jMaterialAccessor;
0101     // Assume globally indexed first
0102     jMaterialAccessor["type"] = "globally_indexed";
0103 
0104     // If we have a globally indexed map, the material data is loaded elsewhere,
0105     // locally indexed material vectors are written though
0106     const auto& materialAccessor = indexedMaterial->materialAccessor();
0107 
0108     if constexpr (std::is_same_v<decltype(materialAccessor),
0109                                  const Acts::IndexedMaterialAccessor&>) {
0110       // It's actually locally indexed
0111       jMaterialAccessor["type"] = "indexed";
0112 
0113       nlohmann::json jMaterialData;
0114       for (const auto& msl : materialAccessor.material) {
0115         jMaterialData.push_back(msl);
0116       }
0117       jMaterialAccessor["storage_vector"] = jMaterialData;
0118     }
0119     // Write the index grid
0120     jMaterialAccessor["grid"] =
0121         Acts::GridJsonConverter::toJson(indexedMaterial->grid());
0122     jMaterial["accessor"] = jMaterialAccessor;
0123 
0124     // Global and bound -> grid local
0125     jMaterial["global_to_grid_local"] = Acts::GridAccessJsonConverter::toJson(
0126         *(indexedMaterial->globalToGridLocal().instance()));
0127 
0128     jMaterial["bound_to_grid_local"] = Acts::GridAccessJsonConverter::toJson(
0129         *(indexedMaterial->boundToGridLocal().instance()));
0130   }
0131 }
0132 
0133 /// @brief Unrolling function for catching the right instance
0134 ///
0135 /// @param jMaterial is the json object to be written into
0136 /// @param indexedMaterial is the indexed material
0137 template <typename... Args>
0138 void unrollIndexedGridConversion(nlohmann::json& jMaterial,
0139                                  const Acts::ISurfaceMaterial& indexedMaterial,
0140                                  Acts::TypeList<Args...> /*unused*/) {
0141   (convertIndexedGridMaterial<Args>(jMaterial, indexedMaterial), ...);
0142 }
0143 
0144 template <typename IndexedAccessorType>
0145 Acts::ISurfaceMaterial* indexedMaterialFromJson(nlohmann::json& jMaterial) {
0146   // Load accessor and grid
0147   nlohmann::json jMaterialAccessor = jMaterial["accessor"];
0148 
0149   // Prepare the material and its accessor
0150   IndexedAccessorType materialAccessor{};
0151 
0152   // If it's locally indexed, we need to load the material vector
0153   if constexpr (std::is_same_v<IndexedAccessorType,
0154                                Acts::IndexedMaterialAccessor>) {
0155     // It's actually locally indexed
0156     for (const auto& msl : jMaterialAccessor["storage_vector"]) {
0157       materialAccessor.material.push_back(msl);
0158     }
0159   }
0160 
0161   // Now make the grid and the axes
0162   nlohmann::json jGrid = jMaterialAccessor["grid"];
0163   nlohmann::json jGridAxes = jGrid["axes"];
0164 
0165   Acts::detail::AxisBoundaryType boundaryType0 = jGridAxes[0]["boundary_type"];
0166 
0167   // 1-dimensional case
0168   if (jGridAxes.size() == 1u) {
0169     // Bound case
0170     if (boundaryType0 == Acts::detail::AxisBoundaryType::Bound) {
0171       Acts::GridAxisGenerators::EqBound eqBound{jGridAxes[0]["range"],
0172                                                 jGridAxes[0]["bins"]};
0173       auto grid =
0174           Acts::GridJsonConverter::fromJson<decltype(eqBound), std::size_t>(
0175               jGrid, eqBound);
0176 
0177       auto boundToGridLocal =
0178           Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson(
0179               jMaterial["bound_to_grid_local"]);
0180 
0181       auto globalToGridLocal =
0182           Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson(
0183               jMaterial["global_to_grid_local"]);
0184 
0185       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0186           std::move(grid), std::move(materialAccessor),
0187           std::move(boundToGridLocal), std::move(globalToGridLocal));
0188     }
0189     // Closed case
0190     if (boundaryType0 == Acts::detail::AxisBoundaryType::Closed) {
0191       Acts::GridAxisGenerators::EqClosed eqClosed{jGridAxes[0]["range"],
0192                                                   jGridAxes[0]["bins"]};
0193       auto grid =
0194           Acts::GridJsonConverter::fromJson<decltype(eqClosed), std::size_t>(
0195               jGrid, eqClosed);
0196 
0197       auto boundToGridLocal =
0198           Acts::GridAccessJsonConverter::boundToGridLocal1DimDelegateFromJson(
0199               jMaterial["bound_to_grid_local"]);
0200 
0201       auto globalToGridLocal =
0202           Acts::GridAccessJsonConverter::globalToGridLocal1DimDelegateFromJson(
0203               jMaterial["global_to_grid_local"]);
0204 
0205       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0206           std::move(grid), std::move(materialAccessor),
0207           std::move(boundToGridLocal), std::move(globalToGridLocal));
0208     }
0209   }
0210 
0211   // 2-dimensional case
0212   if (jGridAxes.size() == 2u) {
0213     // Second boundary type
0214     Acts::detail::AxisBoundaryType boundaryType1 =
0215         jGridAxes[1]["boundary_type"];
0216 
0217     // Bound-bound setup
0218     if (boundaryType0 == Acts::detail::AxisBoundaryType::Bound &&
0219         boundaryType1 == Acts::detail::AxisBoundaryType::Bound) {
0220       Acts::GridAxisGenerators::EqBoundEqBound eqBoundEqBound{
0221           jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"],
0222           jGridAxes[1]["bins"]};
0223       auto grid =
0224           Acts::GridJsonConverter::fromJson<decltype(eqBoundEqBound),
0225                                             std::size_t>(jGrid, eqBoundEqBound);
0226 
0227       auto boundToGridLocal =
0228           Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson(
0229               jMaterial["bound_to_grid_local"]);
0230 
0231       auto globalToGridLocal =
0232           Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson(
0233               jMaterial["global_to_grid_local"]);
0234 
0235       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0236           std::move(grid), std::move(materialAccessor),
0237           std::move(boundToGridLocal), std::move(globalToGridLocal));
0238     }
0239 
0240     // Bound-closed setup
0241     if (boundaryType0 == Acts::detail::AxisBoundaryType::Bound &&
0242         boundaryType1 == Acts::detail::AxisBoundaryType::Closed) {
0243       Acts::GridAxisGenerators::EqBoundEqClosed eqBoundEqClosed{
0244           jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"],
0245           jGridAxes[1]["bins"]};
0246       auto grid = Acts::GridJsonConverter::fromJson<decltype(eqBoundEqClosed),
0247                                                     std::size_t>(
0248           jGrid, eqBoundEqClosed);
0249 
0250       auto boundToGridLocal =
0251           Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson(
0252               jMaterial["bound_to_grid_local"]);
0253 
0254       auto globalToGridLocal =
0255           Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson(
0256               jMaterial["global_to_grid_local"]);
0257 
0258       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0259           std::move(grid), std::move(materialAccessor),
0260           std::move(boundToGridLocal), std::move(globalToGridLocal));
0261     }
0262 
0263     // Closed-bound setup
0264     if (boundaryType0 == Acts::detail::AxisBoundaryType::Closed &&
0265         boundaryType1 == Acts::detail::AxisBoundaryType::Bound) {
0266       Acts::GridAxisGenerators::EqClosedEqBound eqClosedEqBound{
0267           jGridAxes[0]["range"], jGridAxes[0]["bins"], jGridAxes[1]["range"],
0268           jGridAxes[1]["bins"]};
0269       auto grid = Acts::GridJsonConverter::fromJson<decltype(eqClosedEqBound),
0270                                                     std::size_t>(
0271           jGrid, eqClosedEqBound);
0272 
0273       auto boundToGridLocal =
0274           Acts::GridAccessJsonConverter::boundToGridLocal2DimDelegateFromJson(
0275               jMaterial["bound_to_grid_local"]);
0276 
0277       auto globalToGridLocal =
0278           Acts::GridAccessJsonConverter::globalToGridLocal2DimDelegateFromJson(
0279               jMaterial["global_to_grid_local"]);
0280 
0281       return new Acts::IndexedSurfaceMaterial<decltype(grid)>(
0282           std::move(grid), std::move(materialAccessor),
0283           std::move(boundToGridLocal), std::move(globalToGridLocal));
0284     }
0285   }
0286 
0287   return nullptr;
0288 }
0289 
0290 }  // namespace
0291 
0292 void Acts::to_json(nlohmann::json& j, const Material& t) {
0293   if (!t) {
0294     return;
0295   }
0296   for (unsigned i = 0; i < t.parameters().size(); ++i) {
0297     j.push_back(t.parameters()[i]);
0298   }
0299 }
0300 
0301 void Acts::from_json(const nlohmann::json& j, Material& t) {
0302   if (j.is_null()) {
0303     return;
0304   }
0305   Acts::Material::ParametersVector params =
0306       Acts::Material::ParametersVector::Zero();
0307   for (auto i = params.size(); 0 < i--;) {
0308     // .at(...) ensures bound checks
0309     params[i] = j.at(i);
0310   }
0311   t = Acts::Material(params);
0312   return;
0313 }
0314 
0315 void Acts::to_json(nlohmann::json& j, const MaterialSlab& t) {
0316   nlohmann::json jmat(t.material());
0317   j["material"] = jmat;
0318   j["thickness"] = t.thickness();
0319 }
0320 
0321 void Acts::from_json(const nlohmann::json& j, MaterialSlab& t) {
0322   Material mat(j["material"].get<Material>());
0323   t = Acts::MaterialSlab(mat, j.at("thickness").get<float>());
0324 }
0325 
0326 void Acts::from_json(const nlohmann::json& j, MaterialSlabMatrix& t) {
0327   // the input data must be array[array[object]]
0328   for (auto& outer : j) {
0329     Acts::MaterialSlabVector mpVector;
0330     for (auto& inner : outer) {
0331       MaterialSlab mat = inner.get<MaterialSlab>();
0332       mpVector.emplace_back(mat);
0333     }
0334     t.push_back(std::move(mpVector));
0335   }
0336 }
0337 
0338 void Acts::to_json(nlohmann::json& j, const surfaceMaterialPointer& material) {
0339   nlohmann::json jMaterial;
0340   // A bin utility needs to be written
0341   const Acts::BinUtility* bUtility = nullptr;
0342 
0343   // First: Check if we have a proto material
0344   auto psMaterial = dynamic_cast<const Acts::ProtoSurfaceMaterial*>(material);
0345   if (psMaterial != nullptr) {
0346     // Type is proto material
0347     jMaterial[Acts::jsonKey().typekey] = "proto";
0348     // Set mapping type
0349     nlohmann::json mapType(material->mappingType());
0350     jMaterial[Acts::jsonKey().maptype] = mapType;
0351     // by default the protoMaterial is not used for mapping
0352     jMaterial[Acts::jsonKey().mapkey] = false;
0353     // write the bin utility
0354     bUtility = &(psMaterial->binning());
0355     // Check in the number of bin is different from 1
0356     auto& binningData = bUtility->binningData();
0357     for (std::size_t ibin = 0; ibin < binningData.size(); ++ibin) {
0358       if (binningData[ibin].bins() > 1) {
0359         jMaterial[Acts::jsonKey().mapkey] = true;
0360         break;
0361       }
0362     }
0363     nlohmann::json jBin(*bUtility);
0364     jMaterial[Acts::jsonKey().binkey] = jBin;
0365     j[Acts::jsonKey().materialkey] = jMaterial;
0366     return;
0367   }
0368 
0369   // Second: check if we have a homogeneous material
0370   auto hsMaterial =
0371       dynamic_cast<const Acts::HomogeneousSurfaceMaterial*>(material);
0372   if (hsMaterial != nullptr) {
0373     // type is homogeneous
0374     jMaterial[Acts::jsonKey().typekey] = "homogeneous";
0375     // Set mapping type
0376     nlohmann::json mapType(material->mappingType());
0377     jMaterial[Acts::jsonKey().maptype] = mapType;
0378     // Material has been mapped
0379     jMaterial[Acts::jsonKey().mapkey] = true;
0380     nlohmann::json jmat(hsMaterial->materialSlab(Acts::Vector3(0., 0., 0.)));
0381     jMaterial[Acts::jsonKey().datakey] = nlohmann::json::array({
0382         nlohmann::json::array({
0383             jmat,
0384         }),
0385     });
0386     j[Acts::jsonKey().materialkey] = jMaterial;
0387     return;
0388   }
0389 
0390   // Next option remaining: BinnedSurface material
0391   auto bsMaterial = dynamic_cast<const Acts::BinnedSurfaceMaterial*>(material);
0392   if (bsMaterial != nullptr) {
0393     // type is binned
0394     jMaterial[Acts::jsonKey().typekey] = "binned";
0395     // Set mapping type
0396     nlohmann::json mapType(material->mappingType());
0397     jMaterial[Acts::jsonKey().maptype] = mapType;
0398     // Material has been mapped
0399     jMaterial[Acts::jsonKey().mapkey] = true;
0400     bUtility = &(bsMaterial->binUtility());
0401     // convert the data
0402     // get the material matrix
0403     nlohmann::json mmat = nlohmann::json::array();
0404     for (const auto& mpVector : bsMaterial->fullMaterial()) {
0405       nlohmann::json mvec = nlohmann::json::array();
0406       for (const auto& mp : mpVector) {
0407         nlohmann::json jmat(mp);
0408         mvec.push_back(jmat);
0409       }
0410       mmat.push_back(std::move(mvec));
0411     }
0412     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0413     // write the bin utility
0414     nlohmann::json jBin(*bUtility);
0415     jMaterial[Acts::jsonKey().binkey] = jBin;
0416     j[Acts::jsonKey().materialkey] = jMaterial;
0417     return;
0418   }
0419 
0420   // Possible indexed grid types
0421   using IndexedSurfaceGrids = Acts::TypeList<
0422       Acts::IndexedSurfaceMaterial<GridEqBound<std::size_t>>,
0423       Acts::IndexedSurfaceMaterial<GridEqClosed<std::size_t>>,
0424       Acts::IndexedSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0425       Acts::IndexedSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0426       Acts::IndexedSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0427 
0428   unrollIndexedGridConversion(jMaterial, *material, IndexedSurfaceGrids{});
0429   if (!jMaterial.empty()) {
0430     j[Acts::jsonKey().materialkey] = jMaterial;
0431     return;
0432   }
0433 
0434   // Possible: globally indexed grid types
0435   using GloballyIndexedSurfaceGrids = Acts::TypeList<
0436       Acts::GloballyIndexedSurfaceMaterial<GridEqBound<std::size_t>>,
0437       Acts::GloballyIndexedSurfaceMaterial<GridEqClosed<std::size_t>>,
0438       Acts::GloballyIndexedSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0439       Acts::GloballyIndexedSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0440       Acts::GloballyIndexedSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0441 
0442   unrollIndexedGridConversion(jMaterial, *material,
0443                               GloballyIndexedSurfaceGrids{});
0444   if (!jMaterial.empty()) {
0445     j[Acts::jsonKey().materialkey] = jMaterial;
0446     return;
0447   }
0448 
0449   // Possible: material grid types
0450   // using MaterialSurfaceGrids = Acts::TypeList<
0451   //    Acts::GridSurfaceMaterial<GridEqBound<std::size_t>>,
0452   //    Acts::GridSurfaceMaterial<GridEqClosed<std::size_t>>,
0453   //    Acts::GridSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0454   //    Acts::GridSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0455   //    Acts::GridSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0456 
0457   // No material the json object is left empty.
0458   return;
0459 }
0460 
0461 void Acts::from_json(const nlohmann::json& j,
0462                      surfaceMaterialPointer& material) {
0463   if (j.find(Acts::jsonKey().materialkey) == j.end()) {
0464     return;
0465   }
0466   nlohmann::json jMaterial = j[Acts::jsonKey().materialkey];
0467   // By default no material is return.
0468   material = nullptr;
0469   if (jMaterial[Acts::jsonKey().mapkey] == false) {
0470     return;
0471   }
0472 
0473   // Grid based material maps
0474   if (jMaterial[Acts::jsonKey().typekey] == "grid") {
0475     material =
0476         indexedMaterialFromJson<Acts::IndexedMaterialAccessor>(jMaterial);
0477     return;
0478   }
0479 
0480   // The bin utility and material
0481   Acts::BinUtility bUtility;
0482   Acts::MaterialSlabMatrix mpMatrix;
0483   Acts::MappingType mapType = Acts::MappingType::Default;
0484   for (auto& [key, value] : jMaterial.items()) {
0485     if (key == Acts::jsonKey().binkey && !value.empty()) {
0486       from_json(value, bUtility);
0487     }
0488     if (key == Acts::jsonKey().datakey && !value.empty()) {
0489       from_json(value, mpMatrix);
0490     }
0491     if (key == Acts::jsonKey().maptype && !value.empty()) {
0492       from_json(value, mapType);
0493     }
0494   }
0495   // Return the appropriate typr of material
0496   if (mpMatrix.empty()) {
0497     material = new Acts::ProtoSurfaceMaterial(bUtility, mapType);
0498   } else if (bUtility.bins() == 1) {
0499     material = new Acts::HomogeneousSurfaceMaterial(mpMatrix[0][0], mapType);
0500   } else {
0501     material = new Acts::BinnedSurfaceMaterial(bUtility, mpMatrix, mapType);
0502   }
0503 }
0504 
0505 void Acts::to_json(nlohmann::json& j, const volumeMaterialPointer& material) {
0506   nlohmann::json jMaterial;
0507   // A bin utility needs to be written
0508   const Acts::BinUtility* bUtility = nullptr;
0509   // Check if we have a proto material
0510   auto pvMaterial = dynamic_cast<const Acts::ProtoVolumeMaterial*>(material);
0511   if (pvMaterial != nullptr) {
0512     // Type is proto material
0513     jMaterial[Acts::jsonKey().typekey] = "proto";
0514     // By default the protoMaterial is not used for mapping
0515     jMaterial[Acts::jsonKey().mapkey] = false;
0516     bUtility = &(pvMaterial->binUtility());
0517     // Check in the number of bin is different from 1
0518     auto& binningData = bUtility->binningData();
0519     for (std::size_t ibin = 0; ibin < binningData.size(); ++ibin) {
0520       if (binningData[ibin].bins() > 1) {
0521         jMaterial[Acts::jsonKey().mapkey] = true;
0522         break;
0523       }
0524     }
0525     // Write the bin utility
0526     nlohmann::json jBin(*bUtility);
0527     jMaterial[Acts::jsonKey().binkey] = jBin;
0528     j[Acts::jsonKey().materialkey] = jMaterial;
0529     return;
0530   }
0531   // Now check if we have a homogeneous material
0532   auto hvMaterial =
0533       dynamic_cast<const Acts::HomogeneousVolumeMaterial*>(material);
0534   if (hvMaterial != nullptr) {
0535     // type is homogeneous
0536     jMaterial[Acts::jsonKey().typekey] = "homogeneous";
0537     jMaterial[Acts::jsonKey().mapkey] = true;
0538     // array of encoded materials w/ one entry
0539     nlohmann::json jmat(hvMaterial->material({0, 0, 0}));
0540     jMaterial[Acts::jsonKey().datakey] = nlohmann::json::array({
0541         jmat,
0542     });
0543     j[Acts::jsonKey().materialkey] = jMaterial;
0544     return;
0545   }
0546   // Only option remaining: material map
0547   auto bvMaterial2D = dynamic_cast<const Acts::InterpolatedMaterialMap<
0548       Acts::MaterialMapper<Acts::MaterialGrid2D>>*>(material);
0549   // Now check if we have a 2D map
0550   if (bvMaterial2D != nullptr) {
0551     // type is binned
0552     jMaterial[Acts::jsonKey().typekey] = "interpolated2D";
0553     jMaterial[Acts::jsonKey().mapkey] = true;
0554     bUtility = &(bvMaterial2D->binUtility());
0555     // convert the data
0556     nlohmann::json mmat = nlohmann::json::array();
0557     Acts::MaterialGrid2D grid = bvMaterial2D->getMapper().getGrid();
0558     for (std::size_t bin = 0; bin < grid.size(); bin++) {
0559       nlohmann::json jmat(Material(grid.at(bin)));
0560       mmat.push_back(jmat);
0561     }
0562     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0563     // Write the bin utility
0564     nlohmann::json jBin(*bUtility);
0565     jMaterial[Acts::jsonKey().binkey] = jBin;
0566     j[Acts::jsonKey().materialkey] = jMaterial;
0567     return;
0568   }
0569   // Only option remaining: material map
0570   auto bvMaterial3D = dynamic_cast<const Acts::InterpolatedMaterialMap<
0571       Acts::MaterialMapper<Acts::MaterialGrid3D>>*>(material);
0572   // Now check if we have a 3D map
0573   if (bvMaterial3D != nullptr) {
0574     // type is binned
0575     jMaterial[Acts::jsonKey().typekey] = "interpolated3D";
0576     jMaterial[Acts::jsonKey().mapkey] = true;
0577     bUtility = &(bvMaterial3D->binUtility());
0578     // convert the data
0579     nlohmann::json mmat = nlohmann::json::array();
0580     Acts::MaterialGrid3D grid = bvMaterial3D->getMapper().getGrid();
0581     for (std::size_t bin = 0; bin < grid.size(); bin++) {
0582       nlohmann::json jmat(Material(grid.at(bin)));
0583       mmat.push_back(jmat);
0584     }
0585     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0586     // Write the bin utility
0587     nlohmann::json jBin(*bUtility);
0588     jMaterial[Acts::jsonKey().binkey] = jBin;
0589     j[Acts::jsonKey().materialkey] = jMaterial;
0590     return;
0591   }
0592 }
0593 
0594 void Acts::from_json(const nlohmann::json& j, volumeMaterialPointer& material) {
0595   if (j.find(Acts::jsonKey().materialkey) == j.end()) {
0596     return;
0597   }
0598   nlohmann::json jMaterial = j[Acts::jsonKey().materialkey];
0599   // By default no material is return.
0600   material = nullptr;
0601   if (jMaterial[Acts::jsonKey().mapkey] == false) {
0602     return;
0603   }
0604   // The bin utility and material
0605   Acts::BinUtility bUtility;
0606   std::vector<Acts::Material> mmat;
0607   for (auto& [key, value] : jMaterial.items()) {
0608     if (key == Acts::jsonKey().binkey && !value.empty()) {
0609       from_json(value, bUtility);
0610     }
0611     if (key == Acts::jsonKey().datakey && !value.empty()) {
0612       for (const auto& bin : value) {
0613         Acts::Material mat(bin.get<Acts::Material>());
0614         mmat.push_back(mat);
0615       }
0616     }
0617   }
0618   // We have protoMaterial
0619   if (mmat.empty()) {
0620     material = new Acts::ProtoVolumeMaterial(bUtility);
0621     return;
0622   }
0623   if (mmat.size() == 1) {
0624     material = new Acts::HomogeneousVolumeMaterial(mmat[0]);
0625     return;
0626   }
0627   if (bUtility.dimensions() == 2) {
0628     std::function<Acts::Vector2(Acts::Vector3)> transfoGlobalToLocal;
0629     Acts::Grid2D grid = createGrid2D(bUtility, transfoGlobalToLocal);
0630 
0631     Acts::Grid2D::point_t min = grid.minPosition();
0632     Acts::Grid2D::point_t max = grid.maxPosition();
0633     Acts::Grid2D::index_t nBins = grid.numLocalBins();
0634 
0635     Acts::EAxis axis1(min[0], max[0], nBins[0]);
0636     Acts::EAxis axis2(min[1], max[1], nBins[1]);
0637 
0638     // Build the grid and fill it with data
0639     Acts::MaterialGrid2D mGrid(std::make_tuple(axis1, axis2));
0640 
0641     for (std::size_t bin = 0; bin < mmat.size(); bin++) {
0642       mGrid.at(bin) = mmat[bin].parameters();
0643     }
0644     Acts::MaterialMapper<Acts::MaterialGrid2D> matMap(transfoGlobalToLocal,
0645                                                       mGrid);
0646     material = new Acts::InterpolatedMaterialMap<
0647         Acts::MaterialMapper<Acts::MaterialGrid2D>>(std::move(matMap),
0648                                                     bUtility);
0649     return;
0650   }
0651   if (bUtility.dimensions() == 3) {
0652     std::function<Acts::Vector3(Acts::Vector3)> transfoGlobalToLocal;
0653     Acts::Grid3D grid = createGrid3D(bUtility, transfoGlobalToLocal);
0654 
0655     Acts::Grid3D::point_t min = grid.minPosition();
0656     Acts::Grid3D::point_t max = grid.maxPosition();
0657     Acts::Grid3D::index_t nBins = grid.numLocalBins();
0658 
0659     Acts::EAxis axis1(min[0], max[0], nBins[0]);
0660     Acts::EAxis axis2(min[1], max[1], nBins[1]);
0661     Acts::EAxis axis3(min[2], max[2], nBins[2]);
0662 
0663     // Build the grid and fill it with data
0664     Acts::MaterialGrid3D mGrid(std::make_tuple(axis1, axis2, axis3));
0665 
0666     for (std::size_t bin = 0; bin < mmat.size(); bin++) {
0667       mGrid.at(bin) = mmat[bin].parameters();
0668     }
0669     Acts::MaterialMapper<Acts::MaterialGrid3D> matMap(transfoGlobalToLocal,
0670                                                       mGrid);
0671     material = new Acts::InterpolatedMaterialMap<
0672         Acts::MaterialMapper<Acts::MaterialGrid3D>>(std::move(matMap),
0673                                                     bUtility);
0674     return;
0675   }
0676 }