Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-10 08:20:58

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