Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:15:18

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/ProtoSurfaceMaterial.hpp"
0022 #include "Acts/Material/ProtoVolumeMaterial.hpp"
0023 #include "Acts/Plugins/Json/GeometryJsonKeys.hpp"
0024 #include "Acts/Plugins/Json/GridJsonConverter.hpp"
0025 #include "Acts/Plugins/Json/UtilitiesJsonConverter.hpp"
0026 #include "Acts/Surfaces/Surface.hpp"
0027 #include "Acts/Utilities/BinUtility.hpp"
0028 #include "Acts/Utilities/Grid.hpp"
0029 #include "Acts/Utilities/GridAccessHelpers.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 <iosfwd>
0037 #include <memory>
0038 #include <numbers>
0039 #include <stdexcept>
0040 #include <string>
0041 #include <tuple>
0042 #include <utility>
0043 #include <vector>
0044 
0045 namespace {
0046 
0047 // Grid definition : eq bound
0048 template <typename value_type>
0049 using GridEqBound =
0050     Acts::Grid<value_type, Acts::Axis<Acts::AxisType::Equidistant,
0051                                       Acts::AxisBoundaryType::Bound>>;
0052 // Grid definition : eq closed
0053 template <typename value_type>
0054 using GridEqClosed =
0055     Acts::Grid<value_type, Acts::Axis<Acts::AxisType::Equidistant,
0056                                       Acts::AxisBoundaryType::Closed>>;
0057 
0058 // Grid definition : eq bound eq bound
0059 template <typename value_type>
0060 using GridEqBoundEqBound = Acts::Grid<
0061     value_type,
0062     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>,
0063     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>>;
0064 
0065 // Grid definition : eq bound eq closed
0066 template <typename value_type>
0067 using GridEqBoundEqClosed = Acts::Grid<
0068     value_type,
0069     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>,
0070     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Closed>>;
0071 
0072 // Grid definition : eq closed eq bound
0073 template <typename value_type>
0074 using GridEqClosedEqBound = Acts::Grid<
0075     value_type,
0076     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Closed>,
0077     Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>>;
0078 
0079 /// @brief Helper function to convert a grid surface material to json
0080 ///
0081 /// @tparam indexed_grid_materital_t
0082 /// @param jMaterial the json object to written into
0083 /// @param indexedMaterialCandidate the actual indexed material
0084 template <typename indexed_grid_materital_t>
0085 void convertIndexedGridMaterial(
0086     nlohmann::json& jMaterial,
0087     const Acts::ISurfaceMaterial& indexedMaterialCandidate) {
0088   // Check if the material is of the right type
0089   const indexed_grid_materital_t* indexedMaterial =
0090       dynamic_cast<const indexed_grid_materital_t*>(&indexedMaterialCandidate);
0091 
0092   if (indexedMaterial != nullptr) {
0093     // It is a grid type material
0094     jMaterial[Acts::jsonKey().typekey] = "grid";
0095     nlohmann::json jMaterialAccessor;
0096     // Assume globally indexed first
0097     jMaterialAccessor["type"] = "globally_indexed";
0098 
0099     // If we have a globally indexed map, the material data is loaded elsewhere,
0100     // locally indexed material vectors are written though
0101     const auto& materialAccessor = indexedMaterial->materialAccessor();
0102 
0103     if constexpr (std::is_same_v<decltype(materialAccessor),
0104                                  const Acts::IndexedMaterialAccessor&>) {
0105       // It's actually locally indexed
0106       jMaterialAccessor["type"] = "indexed";
0107 
0108       nlohmann::json jMaterialData;
0109       for (const auto& msl : materialAccessor.material) {
0110         jMaterialData.push_back(msl);
0111       }
0112       jMaterialAccessor["storage_vector"] = jMaterialData;
0113     }
0114     // Write the index grid
0115     jMaterialAccessor["grid"] =
0116         Acts::GridJsonConverter::toJson(indexedMaterial->grid());
0117     jMaterial["accessor"] = jMaterialAccessor;
0118 
0119     // Global and bound -> grid local
0120     jMaterial["global_to_grid_local"] = Acts::GridAccessJsonConverter::toJson(
0121         *(indexedMaterial->globalToGridLocal().instance()));
0122 
0123     jMaterial["bound_to_grid_local"] = Acts::GridAccessJsonConverter::toJson(
0124         *(indexedMaterial->boundToGridLocal().instance()));
0125   }
0126 }
0127 
0128 /// @brief Unrolling function for catching the right instance
0129 ///
0130 /// @param jMaterial is the json object to be written into
0131 /// @param indexedMaterial is the indexed material
0132 template <typename... Args>
0133 void unrollIndexedGridConversion(nlohmann::json& jMaterial,
0134                                  const Acts::ISurfaceMaterial& indexedMaterial,
0135                                  Acts::TypeList<Args...> /*unused*/) {
0136   (convertIndexedGridMaterial<Args>(jMaterial, indexedMaterial), ...);
0137 }
0138 
0139 template <typename IndexedAccessorType>
0140 Acts::ISurfaceMaterial* indexedMaterialFromJson(nlohmann::json& jMaterial) {
0141   // Load accessor and grid
0142   nlohmann::json jMaterialAccessor = jMaterial["accessor"];
0143 
0144   // Prepare the material and its accessor
0145   IndexedAccessorType materialAccessor{};
0146 
0147   // If it's locally indexed, we need to load the material vector
0148   if constexpr (std::is_same_v<IndexedAccessorType,
0149                                Acts::IndexedMaterialAccessor>) {
0150     // It's actually locally indexed
0151     for (const auto& msl : jMaterialAccessor["storage_vector"]) {
0152       materialAccessor.material.push_back(msl);
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.isValid()) {
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(j["material"].get<Material>());
0317   t = Acts::MaterialSlab(mat, j.at("thickness").get<float>());
0318 }
0319 
0320 void Acts::from_json(const nlohmann::json& j, MaterialSlabMatrix& t) {
0321   // the input data must be array[array[object]]
0322   for (auto& outer : j) {
0323     Acts::MaterialSlabVector mpVector;
0324     for (auto& inner : outer) {
0325       MaterialSlab mat = inner.get<MaterialSlab>();
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   // First: Check if we have a proto material
0338   auto psMaterial = dynamic_cast<const Acts::ProtoSurfaceMaterial*>(material);
0339   if (psMaterial != nullptr) {
0340     // Type is proto material
0341     jMaterial[Acts::jsonKey().typekey] = "proto";
0342     // Set mapping type
0343     nlohmann::json mapType(material->mappingType());
0344     jMaterial[Acts::jsonKey().maptype] = mapType;
0345     // by default the protoMaterial is not used for mapping
0346     jMaterial[Acts::jsonKey().mapkey] = false;
0347     // write the bin utility
0348     bUtility = &(psMaterial->binning());
0349     // Check in the number of bin is different from 1
0350     auto& binningData = bUtility->binningData();
0351     for (std::size_t ibin = 0; ibin < binningData.size(); ++ibin) {
0352       if (binningData[ibin].bins() > 1) {
0353         jMaterial[Acts::jsonKey().mapkey] = true;
0354         break;
0355       }
0356     }
0357     nlohmann::json jBin(*bUtility);
0358     jMaterial[Acts::jsonKey().binkey] = jBin;
0359     j[Acts::jsonKey().materialkey] = jMaterial;
0360     return;
0361   }
0362 
0363   // Second: check if we have a homogeneous material
0364   auto hsMaterial =
0365       dynamic_cast<const Acts::HomogeneousSurfaceMaterial*>(material);
0366   if (hsMaterial != nullptr) {
0367     // type is homogeneous
0368     jMaterial[Acts::jsonKey().typekey] = "homogeneous";
0369     // Set mapping type
0370     nlohmann::json mapType(material->mappingType());
0371     jMaterial[Acts::jsonKey().maptype] = mapType;
0372     // Material has been mapped
0373     jMaterial[Acts::jsonKey().mapkey] = true;
0374     nlohmann::json jmat(hsMaterial->materialSlab(Acts::Vector3(0., 0., 0.)));
0375     jMaterial[Acts::jsonKey().datakey] = nlohmann::json::array({
0376         nlohmann::json::array({
0377             jmat,
0378         }),
0379     });
0380     j[Acts::jsonKey().materialkey] = jMaterial;
0381     return;
0382   }
0383 
0384   // Next option remaining: BinnedSurface material
0385   auto bsMaterial = dynamic_cast<const Acts::BinnedSurfaceMaterial*>(material);
0386   if (bsMaterial != nullptr) {
0387     // type is binned
0388     jMaterial[Acts::jsonKey().typekey] = "binned";
0389     // Set mapping type
0390     nlohmann::json mapType(material->mappingType());
0391     jMaterial[Acts::jsonKey().maptype] = mapType;
0392     // Material has been mapped
0393     jMaterial[Acts::jsonKey().mapkey] = true;
0394     bUtility = &(bsMaterial->binUtility());
0395     // convert the data
0396     // get the material matrix
0397     nlohmann::json mmat = nlohmann::json::array();
0398     for (const auto& mpVector : bsMaterial->fullMaterial()) {
0399       nlohmann::json mvec = nlohmann::json::array();
0400       for (const auto& mp : mpVector) {
0401         nlohmann::json jmat(mp);
0402         mvec.push_back(jmat);
0403       }
0404       mmat.push_back(std::move(mvec));
0405     }
0406     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0407     // write the bin utility
0408     nlohmann::json jBin(*bUtility);
0409     jMaterial[Acts::jsonKey().binkey] = jBin;
0410     j[Acts::jsonKey().materialkey] = jMaterial;
0411     return;
0412   }
0413 
0414   // Possible indexed grid types
0415   using IndexedSurfaceGrids = Acts::TypeList<
0416       Acts::IndexedSurfaceMaterial<GridEqBound<std::size_t>>,
0417       Acts::IndexedSurfaceMaterial<GridEqClosed<std::size_t>>,
0418       Acts::IndexedSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0419       Acts::IndexedSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0420       Acts::IndexedSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0421 
0422   unrollIndexedGridConversion(jMaterial, *material, IndexedSurfaceGrids{});
0423   if (!jMaterial.empty()) {
0424     j[Acts::jsonKey().materialkey] = jMaterial;
0425     return;
0426   }
0427 
0428   // Possible: globally indexed grid types
0429   using GloballyIndexedSurfaceGrids = Acts::TypeList<
0430       Acts::GloballyIndexedSurfaceMaterial<GridEqBound<std::size_t>>,
0431       Acts::GloballyIndexedSurfaceMaterial<GridEqClosed<std::size_t>>,
0432       Acts::GloballyIndexedSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0433       Acts::GloballyIndexedSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0434       Acts::GloballyIndexedSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0435 
0436   unrollIndexedGridConversion(jMaterial, *material,
0437                               GloballyIndexedSurfaceGrids{});
0438   if (!jMaterial.empty()) {
0439     j[Acts::jsonKey().materialkey] = jMaterial;
0440     return;
0441   }
0442 
0443   // Possible: material grid types
0444   // using MaterialSurfaceGrids = Acts::TypeList<
0445   //    Acts::GridSurfaceMaterial<GridEqBound<std::size_t>>,
0446   //    Acts::GridSurfaceMaterial<GridEqClosed<std::size_t>>,
0447   //    Acts::GridSurfaceMaterial<GridEqBoundEqBound<std::size_t>>,
0448   //    Acts::GridSurfaceMaterial<GridEqBoundEqClosed<std::size_t>>,
0449   //    Acts::GridSurfaceMaterial<GridEqClosedEqBound<std::size_t>>>;
0450 
0451   // No material the json object is left empty.
0452   return;
0453 }
0454 
0455 void Acts::from_json(const nlohmann::json& j,
0456                      surfaceMaterialPointer& material) {
0457   if (j.find(Acts::jsonKey().materialkey) == j.end()) {
0458     return;
0459   }
0460   nlohmann::json jMaterial = j[Acts::jsonKey().materialkey];
0461   // By default no material is return.
0462   material = nullptr;
0463   if (jMaterial[Acts::jsonKey().mapkey] == false) {
0464     return;
0465   }
0466 
0467   // Grid based material maps
0468   if (jMaterial[Acts::jsonKey().typekey] == "grid") {
0469     material =
0470         indexedMaterialFromJson<Acts::IndexedMaterialAccessor>(jMaterial);
0471     return;
0472   }
0473 
0474   // The bin utility and material
0475   Acts::BinUtility bUtility;
0476   Acts::MaterialSlabMatrix mpMatrix;
0477   Acts::MappingType mapType = Acts::MappingType::Default;
0478   for (auto& [key, value] : jMaterial.items()) {
0479     if (key == Acts::jsonKey().binkey && !value.empty()) {
0480       from_json(value, bUtility);
0481     }
0482     if (key == Acts::jsonKey().datakey && !value.empty()) {
0483       from_json(value, mpMatrix);
0484     }
0485     if (key == Acts::jsonKey().maptype && !value.empty()) {
0486       from_json(value, mapType);
0487     }
0488   }
0489   // Return the appropriate typr of material
0490   if (mpMatrix.empty()) {
0491     material = new Acts::ProtoSurfaceMaterial(bUtility, mapType);
0492   } else if (bUtility.bins() == 1) {
0493     material = new Acts::HomogeneousSurfaceMaterial(mpMatrix[0][0], mapType);
0494   } else {
0495     material = new Acts::BinnedSurfaceMaterial(bUtility, mpMatrix, mapType);
0496   }
0497 }
0498 
0499 void Acts::to_json(nlohmann::json& j, const volumeMaterialPointer& material) {
0500   nlohmann::json jMaterial;
0501   // A bin utility needs to be written
0502   const Acts::BinUtility* bUtility = nullptr;
0503   // Check if we have a proto material
0504   auto pvMaterial = dynamic_cast<const Acts::ProtoVolumeMaterial*>(material);
0505   if (pvMaterial != nullptr) {
0506     // Type is proto material
0507     jMaterial[Acts::jsonKey().typekey] = "proto";
0508     // By default the protoMaterial is not used for mapping
0509     jMaterial[Acts::jsonKey().mapkey] = false;
0510     bUtility = &(pvMaterial->binUtility());
0511     // Check in the number of bin is different from 1
0512     auto& binningData = bUtility->binningData();
0513     for (std::size_t ibin = 0; ibin < binningData.size(); ++ibin) {
0514       if (binningData[ibin].bins() > 1) {
0515         jMaterial[Acts::jsonKey().mapkey] = true;
0516         break;
0517       }
0518     }
0519     // Write the bin utility
0520     nlohmann::json jBin(*bUtility);
0521     jMaterial[Acts::jsonKey().binkey] = jBin;
0522     j[Acts::jsonKey().materialkey] = jMaterial;
0523     return;
0524   }
0525   // Now check if we have a homogeneous material
0526   auto hvMaterial =
0527       dynamic_cast<const Acts::HomogeneousVolumeMaterial*>(material);
0528   if (hvMaterial != nullptr) {
0529     // type is homogeneous
0530     jMaterial[Acts::jsonKey().typekey] = "homogeneous";
0531     jMaterial[Acts::jsonKey().mapkey] = true;
0532     // array of encoded materials w/ one entry
0533     nlohmann::json jmat(hvMaterial->material({0, 0, 0}));
0534     jMaterial[Acts::jsonKey().datakey] = nlohmann::json::array({
0535         jmat,
0536     });
0537     j[Acts::jsonKey().materialkey] = jMaterial;
0538     return;
0539   }
0540   // Only option remaining: material map
0541   auto bvMaterial2D = dynamic_cast<const Acts::InterpolatedMaterialMap<
0542       Acts::MaterialMapper<Acts::MaterialGrid2D>>*>(material);
0543   // Now check if we have a 2D map
0544   if (bvMaterial2D != nullptr) {
0545     // type is binned
0546     jMaterial[Acts::jsonKey().typekey] = "interpolated2D";
0547     jMaterial[Acts::jsonKey().mapkey] = true;
0548     bUtility = &(bvMaterial2D->binUtility());
0549     // convert the data
0550     nlohmann::json mmat = nlohmann::json::array();
0551     Acts::MaterialGrid2D grid = bvMaterial2D->getMapper().getGrid();
0552     for (std::size_t bin = 0; bin < grid.size(); bin++) {
0553       nlohmann::json jmat(Material(grid.at(bin)));
0554       mmat.push_back(jmat);
0555     }
0556     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0557     // Write the bin utility
0558     nlohmann::json jBin(*bUtility);
0559     jMaterial[Acts::jsonKey().binkey] = jBin;
0560     j[Acts::jsonKey().materialkey] = jMaterial;
0561     return;
0562   }
0563   // Only option remaining: material map
0564   auto bvMaterial3D = dynamic_cast<const Acts::InterpolatedMaterialMap<
0565       Acts::MaterialMapper<Acts::MaterialGrid3D>>*>(material);
0566   // Now check if we have a 3D map
0567   if (bvMaterial3D != nullptr) {
0568     // type is binned
0569     jMaterial[Acts::jsonKey().typekey] = "interpolated3D";
0570     jMaterial[Acts::jsonKey().mapkey] = true;
0571     bUtility = &(bvMaterial3D->binUtility());
0572     // convert the data
0573     nlohmann::json mmat = nlohmann::json::array();
0574     Acts::MaterialGrid3D grid = bvMaterial3D->getMapper().getGrid();
0575     for (std::size_t bin = 0; bin < grid.size(); bin++) {
0576       nlohmann::json jmat(Material(grid.at(bin)));
0577       mmat.push_back(jmat);
0578     }
0579     jMaterial[Acts::jsonKey().datakey] = std::move(mmat);
0580     // Write the bin utility
0581     nlohmann::json jBin(*bUtility);
0582     jMaterial[Acts::jsonKey().binkey] = jBin;
0583     j[Acts::jsonKey().materialkey] = jMaterial;
0584     return;
0585   }
0586 }
0587 
0588 void Acts::from_json(const nlohmann::json& j, volumeMaterialPointer& material) {
0589   if (j.find(Acts::jsonKey().materialkey) == j.end()) {
0590     return;
0591   }
0592   nlohmann::json jMaterial = j[Acts::jsonKey().materialkey];
0593   // By default no material is return.
0594   material = nullptr;
0595   if (jMaterial[Acts::jsonKey().mapkey] == false) {
0596     return;
0597   }
0598   // The bin utility and material
0599   Acts::BinUtility bUtility;
0600   std::vector<Acts::Material> mmat;
0601   for (auto& [key, value] : jMaterial.items()) {
0602     if (key == Acts::jsonKey().binkey && !value.empty()) {
0603       from_json(value, bUtility);
0604     }
0605     if (key == Acts::jsonKey().datakey && !value.empty()) {
0606       for (const auto& bin : value) {
0607         Acts::Material mat(bin.get<Acts::Material>());
0608         mmat.push_back(mat);
0609       }
0610     }
0611   }
0612   // We have protoMaterial
0613   if (mmat.empty()) {
0614     material = new Acts::ProtoVolumeMaterial(bUtility);
0615     return;
0616   }
0617   if (mmat.size() == 1) {
0618     material = new Acts::HomogeneousVolumeMaterial(mmat[0]);
0619     return;
0620   }
0621   if (bUtility.dimensions() == 2) {
0622     std::function<Acts::Vector2(Acts::Vector3)> transfoGlobalToLocal;
0623     Acts::Grid2D grid = createGrid2D(bUtility, transfoGlobalToLocal);
0624 
0625     Acts::Grid2D::point_t min = grid.minPosition();
0626     Acts::Grid2D::point_t max = grid.maxPosition();
0627     Acts::Grid2D::index_t nBins = grid.numLocalBins();
0628 
0629     Acts::EAxis axis1(min[0], max[0], nBins[0]);
0630     Acts::EAxis axis2(min[1], max[1], nBins[1]);
0631 
0632     // Build the grid and fill it with data
0633     Acts::MaterialGrid2D mGrid(std::make_tuple(axis1, axis2));
0634 
0635     for (std::size_t bin = 0; bin < mmat.size(); bin++) {
0636       mGrid.at(bin) = mmat[bin].parameters();
0637     }
0638     Acts::MaterialMapper<Acts::MaterialGrid2D> matMap(transfoGlobalToLocal,
0639                                                       mGrid);
0640     material = new Acts::InterpolatedMaterialMap<
0641         Acts::MaterialMapper<Acts::MaterialGrid2D>>(std::move(matMap),
0642                                                     bUtility);
0643     return;
0644   }
0645   if (bUtility.dimensions() == 3) {
0646     std::function<Acts::Vector3(Acts::Vector3)> transfoGlobalToLocal;
0647     Acts::Grid3D grid = createGrid3D(bUtility, transfoGlobalToLocal);
0648 
0649     Acts::Grid3D::point_t min = grid.minPosition();
0650     Acts::Grid3D::point_t max = grid.maxPosition();
0651     Acts::Grid3D::index_t nBins = grid.numLocalBins();
0652 
0653     Acts::EAxis axis1(min[0], max[0], nBins[0]);
0654     Acts::EAxis axis2(min[1], max[1], nBins[1]);
0655     Acts::EAxis axis3(min[2], max[2], nBins[2]);
0656 
0657     // Build the grid and fill it with data
0658     Acts::MaterialGrid3D mGrid(std::make_tuple(axis1, axis2, axis3));
0659 
0660     for (std::size_t bin = 0; bin < mmat.size(); bin++) {
0661       mGrid.at(bin) = mmat[bin].parameters();
0662     }
0663     Acts::MaterialMapper<Acts::MaterialGrid3D> matMap(transfoGlobalToLocal,
0664                                                       mGrid);
0665     material = new Acts::InterpolatedMaterialMap<
0666         Acts::MaterialMapper<Acts::MaterialGrid3D>>(std::move(matMap),
0667                                                     bUtility);
0668     return;
0669   }
0670 }
0671 
0672 nlohmann::json Acts::MaterialJsonConverter::toJsonDetray(
0673     const Acts::ISurfaceMaterial& surfaceMaterial, const Acts::Surface& surface,
0674     std::size_t surfaceIndex, std::map<std::size_t, std::size_t>& gridLink) {
0675   nlohmann::json jSurfaceMaterial;
0676 
0677   // Binned material conversion
0678   if (auto binnedMaterial =
0679           dynamic_cast<const BinnedSurfaceMaterial*>(&surfaceMaterial);
0680       binnedMaterial != nullptr) {
0681     // BinUtility modifications
0682     bool swapped = false;
0683     // Get the bin utility (make a copy as we may modify it)
0684     // Detray expects 2-dimensional grid, currently supported are
0685     // x-y, r-phi, phi-z
0686     BinUtility bUtility = binnedMaterial->binUtility();
0687     // Turn the bin value into a 2D grid
0688     if (bUtility.dimensions() == 1u) {
0689       if (bUtility.binningData()[0u].binvalue == AxisDirection::AxisR) {
0690         // Turn to R-Phi
0691         bUtility += BinUtility(1u, -std::numbers::pi, std::numbers::pi, closed,
0692                                AxisDirection::AxisPhi);
0693       } else if (bUtility.binningData()[0u].binvalue == AxisDirection::AxisZ) {
0694         // Turn to Phi-Z - swap needed
0695         BinUtility nbUtility(1u, -std::numbers::pi, std::numbers::pi, closed,
0696                              AxisDirection::AxisPhi);
0697         nbUtility += bUtility;
0698         bUtility = std::move(nbUtility);
0699         swapped = true;
0700       } else {
0701         std::runtime_error("Unsupported binning for Detray");
0702       }
0703     } else if (bUtility.dimensions() == 2u &&
0704                bUtility.binningData()[0u].binvalue == AxisDirection::AxisZ &&
0705                bUtility.binningData()[1u].binvalue == AxisDirection::AxisPhi) {
0706       BinUtility nbUtility(bUtility.binningData()[1u]);
0707       nbUtility += BinUtility{bUtility.binningData()[0u]};
0708       bUtility = std::move(nbUtility);
0709       swapped = true;
0710     }
0711 
0712     AxisDirection bVal0 = bUtility.binningData()[0u].binvalue;
0713     AxisDirection bVal1 = bUtility.binningData()[1u].binvalue;
0714 
0715     // Translate into grid index type
0716     int gridIndexType = 0;
0717     if (bVal0 == AxisDirection::AxisR && bVal1 == AxisDirection::AxisPhi) {
0718       gridIndexType = 0;
0719     } else if (bVal0 == AxisDirection::AxisPhi &&
0720                bVal1 == AxisDirection::AxisZ) {
0721       gridIndexType = 3;
0722     } else if (bVal0 == AxisDirection::AxisX && bVal1 == AxisDirection::AxisY) {
0723       gridIndexType = 2;
0724     } else {
0725       std::runtime_error("Unsupported binning for Detray");
0726     }
0727     // Convert the axes
0728     nlohmann::json jAxes = toJsonDetray(bUtility, surface);
0729     // Create  a grid index, i.e. type, index tuple
0730     nlohmann::json jGridLink;
0731     jGridLink["type"] = gridIndexType;
0732     std::size_t gridIndex = 0;
0733     if (gridLink.contains(gridIndexType)) {
0734       std::size_t& fGridIndex = gridLink[gridIndex];
0735       gridIndex = fGridIndex;
0736       fGridIndex++;
0737     } else {
0738       gridLink[gridIndexType] = 1;
0739     }
0740     jGridLink["index"] = gridIndex;
0741 
0742     // The grid data
0743     jSurfaceMaterial["axes"] = jAxes;
0744     jSurfaceMaterial["grid_link"] = jGridLink;
0745     jSurfaceMaterial["owner_link"] = surfaceIndex;
0746 
0747     // The bins to be filled
0748     nlohmann::json jBins;
0749     auto materialMatrix = binnedMaterial->fullMaterial();
0750     for (std::size_t ib1 = 0; ib1 < materialMatrix.size(); ++ib1) {
0751       for (std::size_t ib0 = 0; ib0 < materialMatrix[0u].size(); ++ib0) {
0752         nlohmann::json jBin;
0753         // Look up the material slab
0754         MaterialSlab slab = materialMatrix[ib1][ib0];
0755         // Translate into a local bin
0756         std::size_t lb0 = swapped ? ib1 : ib0;
0757         std::size_t lb1 = swapped ? ib0 : ib1;
0758         jBin["loc_index"] = std::array<std::size_t, 2u>{lb0, lb1};
0759 
0760         const Material& material = slab.material();
0761         // The content
0762         nlohmann::json jContent;
0763         jContent["thickness"] = slab.thickness();
0764         // The actual material
0765         nlohmann::json jMaterialParams;
0766         if (slab.thickness() > 0.) {
0767           jMaterialParams["params"] =
0768               std::vector<double>{material.X0(),
0769                                   material.L0(),
0770                                   material.Ar(),
0771                                   material.Z(),
0772                                   material.massDensity(),
0773                                   material.molarDensity(),
0774                                   0.};
0775 
0776         } else {
0777           jMaterialParams["params"] =
0778               std::vector<double>{0., 0., 0., 0., 0., 0., 0.};
0779         }
0780         jContent["material"] = jMaterialParams;
0781         jContent["type"] = 6;
0782         jContent["surface_idx"] = surfaceIndex;
0783 
0784         nlohmann::json jContentVector;
0785         jContentVector.push_back(jContent);
0786         jBin["content"] = jContentVector;
0787         jBins.push_back(jBin);
0788       }
0789     }
0790     jSurfaceMaterial["bins"] = jBins;
0791   }
0792   return jSurfaceMaterial;
0793 }
0794 
0795 nlohmann::json Acts::MaterialJsonConverter::toJsonDetray(
0796     const Acts::BinUtility& binUtility, const Surface& surface) {
0797   nlohmann::json jAxes;
0798   for (const auto [ib, bData] : enumerate(binUtility.binningData())) {
0799     nlohmann::json jAxis;
0800     jAxis["bounds"] = bData.option == closed ? 2 : 1;
0801     jAxis["binning"] = 0u;
0802     jAxis["label"] = ib;
0803     jAxis["bins"] = bData.bins();
0804     double offset = 0;
0805     if (bData.binvalue == AxisDirection::AxisZ) {
0806       offset = surface.center(Acts::GeometryContext{}).z();
0807     }
0808     jAxis["edges"] =
0809         std::array<double, 2>{bData.min + offset, bData.max + offset};
0810     jAxes.push_back(jAxis);
0811   }
0812   return jAxes;
0813 }