Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-06 08:11:44

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2022 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 <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Detector/ProtoDetector.hpp"
0013 #include "Acts/Geometry/Extent.hpp"
0014 #include "Acts/Plugins/Json/ProtoDetectorJsonConverter.hpp"
0015 #include "Acts/Surfaces/Surface.hpp"
0016 #include "Acts/Utilities/BinningData.hpp"
0017 #include "Acts/Utilities/BinningType.hpp"
0018 #include "Acts/Utilities/Enumerate.hpp"
0019 
0020 #include <array>
0021 #include <cmath>
0022 #include <fstream>
0023 #include <iterator>
0024 #include <optional>
0025 #include <string>
0026 #include <vector>
0027 
0028 #include <nlohmann/json.hpp>
0029 
0030 #include "EqualityHelpers.hpp"
0031 
0032 using namespace Acts;
0033 
0034 namespace {
0035 
0036 /// @brief Helper method to compare proto volumes
0037 /// @param one the first volume object
0038 /// @param two the second volume object
0039 /// @param tolerance the tolerance
0040 /// @return a boolean to see if they are equal
0041 bool isEqual(const Acts::ProtoVolume& one, const Acts::ProtoVolume& two,
0042              const Acts::ActsScalar tolerance = 0.) {
0043   bool nameEq = (one.name == two.name);
0044   // Name
0045   BOOST_CHECK(nameEq);
0046   // Extent
0047   bool extentEq = isEqual(one.extent, two.extent, tolerance);
0048   BOOST_CHECK(extentEq);
0049 
0050   // Check internal structure
0051   bool internalValueEq = (one.internal.has_value() == two.internal.has_value());
0052   BOOST_CHECK(internalValueEq);
0053   bool internalEq = internalValueEq;
0054   if (one.internal.has_value() && two.internal.has_value()) {
0055     // Check consistency of the internal structure
0056     const auto& itsOne = one.internal.value();
0057     const auto& itsTwo = two.internal.value();
0058     bool layerTypeEq = (itsOne.layerType == itsTwo.layerType);
0059     BOOST_CHECK(layerTypeEq);
0060     internalEq = layerTypeEq;
0061     bool sBinningSizeEq =
0062         (itsOne.surfaceBinning.size() == itsTwo.surfaceBinning.size());
0063     BOOST_CHECK(sBinningSizeEq);
0064     internalEq = internalEq && sBinningSizeEq;
0065     for (auto [isb, sb] : Acts::enumerate(itsOne.surfaceBinning)) {
0066       bool sBinningEq = isEqual(sb, itsTwo.surfaceBinning[isb], tolerance);
0067       BOOST_CHECK(sBinningEq);
0068       internalEq = internalEq && sBinningEq;
0069     }
0070   }
0071   BOOST_CHECK(internalEq);
0072 
0073   // Check container structure
0074   bool containerValueEq =
0075       (one.container.has_value() == two.container.has_value());
0076   BOOST_CHECK(containerValueEq);
0077   bool containerEq = containerValueEq;
0078   if (one.container.has_value() && two.container.has_value()) {
0079     // Check consistency of the container structure
0080     const auto& ctsOne = one.container.value();
0081     const auto& ctsTwo = two.container.value();
0082     bool layerContainerEq = (ctsOne.layerContainer == ctsTwo.layerContainer);
0083     BOOST_CHECK(layerContainerEq);
0084     containerEq = layerContainerEq;
0085     bool cBinningSizeEq =
0086         ctsOne.constituentBinning.size() == ctsTwo.constituentBinning.size();
0087     containerEq = containerEq && cBinningSizeEq;
0088     BOOST_CHECK(cBinningSizeEq);
0089     for (auto [icb, cb] : Acts::enumerate(ctsOne.constituentBinning)) {
0090       bool cBinningEq = isEqual(cb, ctsTwo.constituentBinning[icb], tolerance);
0091       BOOST_CHECK(cBinningEq);
0092       containerEq = containerEq && cBinningEq;
0093     }
0094     // Recursively walk down
0095     bool cSizeEq =
0096         (ctsOne.constituentVolumes.size() == ctsTwo.constituentVolumes.size());
0097     BOOST_CHECK(cSizeEq);
0098     containerEq = cSizeEq;
0099     for (auto [ic, cOne] : Acts::enumerate(ctsOne.constituentVolumes)) {
0100       const auto& cTwo = ctsTwo.constituentVolumes[ic];
0101       bool cEq = isEqual(cOne, cTwo, tolerance);
0102       BOOST_CHECK(cEq);
0103       containerEq = containerEq && cEq;
0104     }
0105   }
0106   BOOST_CHECK(containerEq);
0107 
0108   // Give the overall judgement
0109   return nameEq && extentEq && internalEq && containerEq;
0110 }
0111 
0112 }  // namespace
0113 
0114 BOOST_AUTO_TEST_SUITE(ProtoDetectorJsonConverter)
0115 
0116 BOOST_AUTO_TEST_CASE(ProtoDetectorRoundTrip) {
0117   Acts::ExtentEnvelope cylinderLayerEnvelope = zeroEnvelopes;
0118   cylinderLayerEnvelope[Acts::binR] = {1., 1.};
0119   cylinderLayerEnvelope[Acts::binZ] = {2., 2.};
0120 
0121   Acts::ExtentEnvelope discLayerEnvelope = zeroEnvelopes;
0122   discLayerEnvelope[Acts::binR] = {1., 1.};
0123   discLayerEnvelope[Acts::binZ] = {1., 1.};
0124 
0125   // Beam Pipe container
0126   Acts::ProtoVolume beamPipeContainer;
0127   beamPipeContainer.name = "odd-beam-pipe";
0128   beamPipeContainer.extent.set(Acts::binR, 0., 25);
0129   Acts::ProtoVolume beamPipe;
0130   beamPipe.name = "odd-beam-pipe-l";
0131   beamPipe.extent.set(Acts::binR, 2., 24.);
0132   beamPipe.internal = Acts::ProtoVolume::InternalStructure{
0133       Acts::Surface::SurfaceType::Cylinder};
0134   beamPipeContainer.container = Acts::ProtoVolume::ContainerStructure{
0135       {beamPipe}, {Acts::BinningData(Acts::open, Acts::binR, {0., 1.})}, true};
0136 
0137   // Pixel section
0138   Acts::ProtoVolume pixelContainer;
0139   pixelContainer.name = "odd-pixel";
0140   pixelContainer.extent.set(Acts::binR, 25., 200);
0141 
0142   Acts::ProtoVolume pixelNec;
0143   pixelNec.name = "odd-pixel-nec";
0144   pixelNec.extent.set(Acts::binZ, -3100., -580);
0145 
0146   Acts::ProtoVolume pixNecD6;
0147   pixNecD6.name = "odd-pixel-nec-d6";
0148   pixNecD6.extent.set(Acts::binZ, -1540., -1500);
0149   Acts::ProtoVolume pixNecD5;
0150   pixNecD5.name = "odd-pixel-nec-d5";
0151   pixNecD5.extent.set(Acts::binZ, -1340., -1300);
0152   Acts::ProtoVolume pixNecD4;
0153   pixNecD4.name = "odd-pixel-nec-d4";
0154   pixNecD4.extent.set(Acts::binZ, -1140., -1100);
0155   Acts::ProtoVolume pixNecD3;
0156   pixNecD3.name = "odd-pixel-nec-d3";
0157   pixNecD3.extent.set(Acts::binZ, -1000., -960.);
0158   Acts::ProtoVolume pixNecD2;
0159   pixNecD2.name = "odd-pixel-nec-d2";
0160   pixNecD2.extent.set(Acts::binZ, -860., -820);
0161   Acts::ProtoVolume pixNecD1;
0162   pixNecD1.name = "odd-pixel-nec-d1";
0163   pixNecD1.extent.set(Acts::binZ, -740., -700);
0164   Acts::ProtoVolume pixNecD0;
0165   pixNecD0.name = "odd-pixel-nec-d0";
0166   pixNecD0.extent.set(Acts::binZ, -640., -600);
0167   pixelNec.container = Acts::ProtoVolume::ContainerStructure{
0168       {pixNecD6, pixNecD5, pixNecD4, pixNecD3, pixNecD2, pixNecD1, pixNecD0},
0169       {Acts::BinningData(Acts::open, Acts::binZ, {0., 1.})},
0170       true};
0171 
0172   Acts::BinningData pixEcBinningR =
0173       Acts::BinningData(Acts::open, Acts::binR, 2., 0., 1.);
0174   Acts::BinningData pixEcBinningPhi =
0175       Acts::BinningData(Acts::closed, Acts::binPhi, 30., -M_PI, M_PI);
0176 
0177   for (auto& cv : pixelNec.container.value().constituentVolumes) {
0178     cv.extent.setEnvelope(discLayerEnvelope);
0179     cv.internal = Acts::ProtoVolume::InternalStructure{
0180         Acts::Surface::SurfaceType::Disc, {pixEcBinningR, pixEcBinningPhi}};
0181   }
0182 
0183   Acts::ProtoVolume pixelBarrel;
0184   pixelBarrel.name = "odd-pixel-barrel";
0185   pixelBarrel.extent.set(Acts::binZ, -580., 580);
0186 
0187   Acts::ProtoVolume pixBarrelL0;
0188   pixBarrelL0.name = "odd-pixel-barrel-l0";
0189   pixBarrelL0.extent.set(Acts::binR, 28., 48.);
0190   pixBarrelL0.extent.set(Acts::binZ, -580., 580);
0191   Acts::ProtoVolume pixBarrelL1;
0192   pixBarrelL1.name = "odd-pixel-barrel-l1";
0193   pixBarrelL1.extent.set(Acts::binR, 62., 76);
0194   pixBarrelL1.extent.set(Acts::binZ, -580., 580);
0195   Acts::ProtoVolume pixBarrelL2;
0196   pixBarrelL2.name = "odd-pixel-barrel-l2";
0197   pixBarrelL2.extent.set(Acts::binR, 100., 120.);
0198   pixBarrelL2.extent.set(Acts::binZ, -580., 580);
0199   Acts::ProtoVolume pixBarrelL3;
0200   pixBarrelL3.name = "odd-pixel-barrel-l3";
0201   pixBarrelL3.extent.set(Acts::binR, 160., 180.);
0202   pixBarrelL3.extent.set(Acts::binZ, -580., 580);
0203 
0204   pixelBarrel.container = Acts::ProtoVolume::ContainerStructure{
0205       {pixBarrelL0, pixBarrelL1, pixBarrelL2, pixBarrelL3},
0206       {Acts::BinningData(Acts::open, Acts::binR, {0., 1})},
0207       true};
0208 
0209   for (auto& cv : pixelBarrel.container.value().constituentVolumes) {
0210     cv.extent.setEnvelope(cylinderLayerEnvelope);
0211     cv.internal = Acts::ProtoVolume::InternalStructure{
0212         Acts::Surface::SurfaceType::Cylinder};
0213   }
0214 
0215   Acts::ProtoVolume pixelPec;
0216   pixelPec.name = "odd-pixel-pec";
0217   pixelPec.extent.set(Acts::binZ, 580., 3100.);
0218 
0219   Acts::ProtoVolume pixPecD0;
0220   pixPecD0.name = "odd-pixel-pec-d0";
0221   pixPecD0.extent.set(Acts::binZ, 600., 640);
0222   Acts::ProtoVolume pixPecD1;
0223   pixPecD1.name = "odd-pixel-pec-d1";
0224   pixPecD1.extent.set(Acts::binZ, 700., 740);
0225   Acts::ProtoVolume pixPecD2;
0226   pixPecD2.name = "odd-pixel-pec-d2";
0227   pixPecD2.extent.set(Acts::binZ, 820., 860.);
0228   Acts::ProtoVolume pixPecD3;
0229   pixPecD3.name = "odd-pixel-pec-d3";
0230   pixPecD3.extent.set(Acts::binZ, 960., 1000.);
0231   Acts::ProtoVolume pixPecD4;
0232   pixPecD4.name = "odd-pixel-pec-d4";
0233   pixPecD4.extent.set(Acts::binZ, 1100., 1140);
0234   Acts::ProtoVolume pixPecD5;
0235   pixPecD5.name = "odd-pixel-pec-d5";
0236   pixPecD5.extent.set(Acts::binZ, 1300., 1340.);
0237   Acts::ProtoVolume pixPecD6;
0238   pixPecD6.name = "odd-pixel-pec-d6";
0239   pixPecD6.extent.set(Acts::binZ, 1500., 1540.);
0240 
0241   pixelPec.container = Acts::ProtoVolume::ContainerStructure{
0242       {pixPecD0, pixPecD1, pixPecD2, pixPecD3, pixPecD4, pixPecD5, pixPecD6},
0243       {Acts::BinningData(Acts::open, Acts::binZ, {0., 1})},
0244       true};
0245 
0246   for (auto& cv : pixelPec.container.value().constituentVolumes) {
0247     cv.extent.setEnvelope(discLayerEnvelope);
0248     cv.internal = Acts::ProtoVolume::InternalStructure{
0249         Acts::Surface::SurfaceType::Disc, {pixEcBinningR, pixEcBinningPhi}};
0250   }
0251 
0252   pixelContainer.container = Acts::ProtoVolume::ContainerStructure{
0253       {pixelNec, pixelBarrel, pixelPec},
0254       {Acts::BinningData(Acts::open, Acts::binZ,
0255                          {-3100., -580., 580., 3100.})}};
0256 
0257   // Short Strip section
0258   Acts::ProtoVolume pstContainer;
0259   pstContainer.name = "odd-pst";
0260   pstContainer.extent.set(Acts::binR, 200., 210.);
0261   Acts::ProtoVolume pst;
0262   pst.name = "odd-pst-l";
0263   pst.extent.set(Acts::binR, 201., 209.);
0264   pst.internal = Acts::ProtoVolume::InternalStructure{
0265       Acts::Surface::SurfaceType::Cylinder};
0266   pstContainer.container = Acts::ProtoVolume::ContainerStructure{
0267       {pst}, {Acts::BinningData(Acts::open, Acts::binR, {0., 1.})}, true};
0268 
0269   // Short Strip section
0270   Acts::ProtoVolume sstripContainer;
0271   sstripContainer.name = "odd-sstrip";
0272   sstripContainer.extent.set(Acts::binR, 210., 720);
0273 
0274   Acts::BinningData sstripEcBinningR =
0275       Acts::BinningData(Acts::open, Acts::binR, 3., 0., 1.);
0276   Acts::BinningData sstripEcBinningPhi =
0277       Acts::BinningData(Acts::closed, Acts::binPhi, 42., -M_PI, M_PI);
0278 
0279   Acts::ProtoVolume sstripNec;
0280   sstripNec.name = "odd-sstrip-nec";
0281   sstripNec.extent.set(Acts::binZ, -3100., -1200);
0282   Acts::ProtoVolume sstripNecD5;
0283   sstripNecD5.name = "odd-sstrip-nec-d5";
0284   sstripNecD5.extent.set(Acts::binZ, -3000, -2900.);
0285   Acts::ProtoVolume sstripNecD4;
0286   sstripNecD4.name = "odd-sstrip-nec-d4";
0287   sstripNecD4.extent.set(Acts::binZ, -2600., -2500.);
0288   Acts::ProtoVolume sstripNecD3;
0289   sstripNecD3.name = "odd-sstrip-nec-d3";
0290   sstripNecD3.extent.set(Acts::binZ, -2250, -2150.);
0291   Acts::ProtoVolume sstripNecD2;
0292   sstripNecD2.name = "odd-sstrip-nec-d2";
0293   sstripNecD2.extent.set(Acts::binZ, -1900, -1800.);
0294   Acts::ProtoVolume sstripNecD1;
0295   sstripNecD1.name = "odd-sstrip-nec-d1";
0296   sstripNecD1.extent.set(Acts::binZ, -1600., -1500.);
0297   Acts::ProtoVolume sstripNecD0;
0298   sstripNecD0.name = "odd-sstrip-nec-d0";
0299   sstripNecD0.extent.set(Acts::binZ, -1350., -1250.);
0300 
0301   sstripNec.container = Acts::ProtoVolume::ContainerStructure{
0302       {sstripNecD5, sstripNecD4, sstripNecD3, sstripNecD2, sstripNecD1,
0303        sstripNecD0},
0304       {Acts::BinningData(Acts::open, Acts::binZ, {0., 1})},
0305       true};
0306 
0307   for (auto& cv : sstripNec.container.value().constituentVolumes) {
0308     cv.extent.setEnvelope(discLayerEnvelope);
0309     cv.internal = Acts::ProtoVolume::InternalStructure{
0310         Acts::Surface::SurfaceType::Disc,
0311         {sstripEcBinningR, sstripEcBinningPhi}};
0312   }
0313 
0314   Acts::ProtoVolume sstripBarrel;
0315   sstripBarrel.name = "odd-sstrip-barrel";
0316   sstripBarrel.extent.set(Acts::binZ, -1200., 1200);
0317 
0318   Acts::ProtoVolume sstripBarrelL0;
0319   sstripBarrelL0.name = "odd-sstrip-barrel-l0";
0320   sstripBarrelL0.extent.set(Acts::binR, 240., 280.);
0321   Acts::ProtoVolume sstripBarrelL1;
0322   sstripBarrelL1.name = "odd-sstrip-barrel-l1";
0323   sstripBarrelL1.extent.set(Acts::binR, 340., 380.);
0324   Acts::ProtoVolume sstripBarrelL2;
0325   sstripBarrelL2.name = "odd-sstrip-barrel-l2";
0326   sstripBarrelL2.extent.set(Acts::binR, 480., 520.);
0327   Acts::ProtoVolume sstripBarrelL3;
0328   sstripBarrelL3.name = "odd-sstrip-barrel-l3";
0329   sstripBarrelL3.extent.set(Acts::binR, 640., 680.);
0330 
0331   sstripBarrel.container = Acts::ProtoVolume::ContainerStructure{
0332       {sstripBarrelL0, sstripBarrelL1, sstripBarrelL2, sstripBarrelL3},
0333       {Acts::BinningData(Acts::open, Acts::binR, {0., 1})},
0334       true};
0335 
0336   for (auto& cv : sstripBarrel.container.value().constituentVolumes) {
0337     cv.extent.setEnvelope(cylinderLayerEnvelope);
0338     cv.internal = Acts::ProtoVolume::InternalStructure{
0339         Acts::Surface::SurfaceType::Cylinder};
0340   }
0341 
0342   Acts::ProtoVolume sstripPec;
0343   sstripPec.name = "odd-sstrip-pec";
0344   sstripPec.extent.set(Acts::binZ, 1200., 3100);
0345 
0346   Acts::ProtoVolume sstripPecD0;
0347   sstripPecD0.name = "odd-sstrip-pec-d0";
0348   sstripPecD0.extent.set(Acts::binZ, 1250., 1350);
0349   Acts::ProtoVolume sstripPecD1;
0350   sstripPecD1.name = "odd-sstrip-pec-d1";
0351   sstripPecD1.extent.set(Acts::binZ, 1500., 1600.);
0352   Acts::ProtoVolume sstripPecD2;
0353   sstripPecD2.name = "odd-sstrip-pec-d2";
0354   sstripPecD2.extent.set(Acts::binZ, 1800., 1900.);
0355   Acts::ProtoVolume sstripPecD3;
0356   sstripPecD3.name = "odd-sstrip-pec-d3";
0357   sstripPecD3.extent.set(Acts::binZ, 2150., 2250.);
0358   Acts::ProtoVolume sstripPecD4;
0359   sstripPecD4.name = "odd-sstrip-pec-d4";
0360   sstripPecD4.extent.set(Acts::binZ, 2500., 2600.);
0361   Acts::ProtoVolume sstripPecD5;
0362   sstripPecD5.name = "odd-sstrip-pec-d5";
0363   sstripPecD5.extent.set(Acts::binZ, 2900., 3000.);
0364 
0365   sstripPec.container = Acts::ProtoVolume::ContainerStructure{
0366       {sstripPecD0, sstripPecD1, sstripPecD2, sstripPecD3, sstripPecD4,
0367        sstripPecD5},
0368       {Acts::BinningData(Acts::open, Acts::binZ, {0., 1})},
0369       true};
0370   for (auto& cv : sstripPec.container.value().constituentVolumes) {
0371     cv.extent.setEnvelope(discLayerEnvelope);
0372     cv.internal = Acts::ProtoVolume::InternalStructure{
0373         Acts::Surface::SurfaceType::Disc,
0374         {sstripEcBinningR, sstripEcBinningPhi}};
0375   }
0376 
0377   sstripContainer.container = Acts::ProtoVolume::ContainerStructure{
0378       {sstripNec, sstripBarrel, sstripPec},
0379       {Acts::BinningData(Acts::open, Acts::binZ,
0380                          {-3100., -1200., 1200., 3100.})}};
0381 
0382   // Long Strip section
0383   Acts::ProtoVolume lstripContainer;
0384   lstripContainer.name = "odd-lstrip";
0385   lstripContainer.extent.set(Acts::binR, 720, 1100.);
0386 
0387   Acts::ProtoVolume lstripNec;
0388   lstripNec.name = "odd-lstrip-nec";
0389   lstripNec.extent.set(Acts::binZ, -3100., -1200);
0390   Acts::ProtoVolume lstripNecD5;
0391   lstripNecD5.name = "odd-lstrip-nec-d5";
0392   lstripNecD5.extent.set(Acts::binZ, -3050, -2900.);
0393   Acts::ProtoVolume lstripNecD4;
0394   lstripNecD4.name = "odd-lstrip-nec-d4";
0395   lstripNecD4.extent.set(Acts::binZ, -2650., -2500.);
0396   Acts::ProtoVolume lstripNecD3;
0397   lstripNecD3.name = "odd-lstrip-nec-d3";
0398   lstripNecD3.extent.set(Acts::binZ, -2300, -2150.);
0399   Acts::ProtoVolume lstripNecD2;
0400   lstripNecD2.name = "odd-lstrip-nec-d2";
0401   lstripNecD2.extent.set(Acts::binZ, -1950, -1800.);
0402   Acts::ProtoVolume lstripNecD1;
0403   lstripNecD1.name = "odd-lstrip-nec-d1";
0404   lstripNecD1.extent.set(Acts::binZ, -1650., -1500.);
0405   Acts::ProtoVolume lstripNecD0;
0406   lstripNecD0.name = "odd-lstrip-nec-d0";
0407   lstripNecD0.extent.set(Acts::binZ, -1400., -1250.);
0408 
0409   lstripNec.container = Acts::ProtoVolume::ContainerStructure{
0410       {lstripNecD5, lstripNecD4, lstripNecD3, lstripNecD2, lstripNecD1,
0411        lstripNecD0},
0412       {Acts::BinningData(Acts::open, Acts::binZ, {0., 1})},
0413       true};
0414 
0415   for (auto& cv : lstripNec.container.value().constituentVolumes) {
0416     cv.extent.setEnvelope(discLayerEnvelope);
0417     cv.internal =
0418         Acts::ProtoVolume::InternalStructure{Acts::Surface::SurfaceType::Disc};
0419   }
0420 
0421   Acts::ProtoVolume lstripBarrel;
0422   lstripBarrel.name = "odd-lstrip-barrel";
0423   lstripBarrel.extent.set(Acts::binZ, -1200., 1200);
0424 
0425   Acts::ProtoVolume lstripBarrelL0;
0426   lstripBarrelL0.name = "odd-lstrip-barrel-l0";
0427   lstripBarrelL0.extent.set(Acts::binR, 800., 840.);
0428   Acts::ProtoVolume lstripBarrelL1;
0429   lstripBarrelL1.name = "odd-lstrip-barrel-l1";
0430   lstripBarrelL1.extent.set(Acts::binR, 1000., 1050.);
0431 
0432   lstripBarrel.container = Acts::ProtoVolume::ContainerStructure{
0433       {lstripBarrelL0, lstripBarrelL1},
0434       {Acts::BinningData(Acts::open, Acts::binR, {0., 1})},
0435       true};
0436 
0437   for (auto& cv : lstripBarrel.container.value().constituentVolumes) {
0438     cv.extent.setEnvelope(cylinderLayerEnvelope);
0439     cv.internal = Acts::ProtoVolume::InternalStructure{
0440         Acts::Surface::SurfaceType::Cylinder};
0441   }
0442 
0443   Acts::ProtoVolume lstripPec;
0444   lstripPec.name = "odd-lstrip-pec";
0445   lstripPec.extent.set(Acts::binZ, 1200., 3100);
0446 
0447   Acts::ProtoVolume lstripPecD0;
0448   lstripPecD0.name = "odd-lstrip-pec-d0";
0449   lstripPecD0.extent.set(Acts::binZ, 1250., 1400);
0450   Acts::ProtoVolume lstripPecD1;
0451   lstripPecD1.name = "odd-lstrip-pec-d1";
0452   lstripPecD1.extent.set(Acts::binZ, 1500., 1650.);
0453   Acts::ProtoVolume lstripPecD2;
0454   lstripPecD2.name = "odd-lstrip-pec-d2";
0455   lstripPecD2.extent.set(Acts::binZ, 1800., 1950.);
0456   Acts::ProtoVolume lstripPecD3;
0457   lstripPecD3.name = "odd-lstrip-pec-d3";
0458   lstripPecD3.extent.set(Acts::binZ, 2150., 2300.);
0459   Acts::ProtoVolume lstripPecD4;
0460   lstripPecD4.name = "odd-lstrip-pec-d4";
0461   lstripPecD4.extent.set(Acts::binZ, 2500., 2650.);
0462   Acts::ProtoVolume lstripPecD5;
0463   lstripPecD5.name = "odd-lstrip-pec-d5";
0464   lstripPecD5.extent.set(Acts::binZ, 2900., 3050.);
0465 
0466   lstripPec.container = Acts::ProtoVolume::ContainerStructure{
0467       {lstripPecD0, lstripPecD1, lstripPecD2, lstripPecD3, lstripPecD4,
0468        lstripPecD5},
0469       {Acts::BinningData(Acts::open, Acts::binZ, {0., 1})},
0470       true};
0471   for (auto& cv : lstripPec.container.value().constituentVolumes) {
0472     cv.internal =
0473         Acts::ProtoVolume::InternalStructure{Acts::Surface::SurfaceType::Disc};
0474     cv.extent.setEnvelope(discLayerEnvelope);
0475   }
0476   lstripContainer.container = Acts::ProtoVolume::ContainerStructure{
0477       {lstripNec, lstripBarrel, lstripPec},
0478       {Acts::BinningData(Acts::open, Acts::binZ,
0479                          {-3100., -1200., 1200., 3100.})}};
0480 
0481   // The overall container
0482   Acts::ProtoVolume detectorContainer;
0483   detectorContainer.name = "odd-light-world";
0484   detectorContainer.extent.set(Acts::binR, 0., 1100.);
0485   detectorContainer.extent.set(Acts::binZ, -3100., 3100.);
0486   detectorContainer.container = Acts::ProtoVolume::ContainerStructure{
0487       {beamPipeContainer, pixelContainer, pstContainer, sstripContainer,
0488        lstripContainer},
0489       {Acts::BinningData(Acts::open, Acts::binR,
0490                          {0., 25., 200., 210., 720., 1100.})}};
0491 
0492   // ----------------------------------------------------------
0493   Acts::ProtoDetector detector;
0494   detector.name = "odd-light";
0495   detector.worldVolume = detectorContainer;
0496 
0497   // Transform into json
0498   nlohmann::json jdet;
0499   jdet["detector"] = detector;
0500 
0501   std::ofstream out;
0502   out.open("odd-proto-detector.json");
0503   out << jdet.dump(4);
0504   out.close();
0505 
0506   Acts::ProtoDetector detectorIn = jdet["detector"];
0507 
0508   // Let's compare
0509   BOOST_CHECK_EQUAL(detector.name, detectorIn.name);
0510 
0511   const auto& world = detector.worldVolume;
0512   const auto& worldIn = detectorIn.worldVolume;
0513 
0514   BOOST_CHECK(isEqual(world, worldIn, 0.1));
0515 }
0516 
0517 BOOST_AUTO_TEST_SUITE_END()