File indexing completed on 2026-05-18 07:49:24
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Direction.hpp"
0012 #include "Acts/Definitions/Units.hpp"
0013 #include "Acts/Geometry/CuboidVolumeBuilder.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/Geometry/TrackingGeometry.hpp"
0016 #include "Acts/Geometry/TrackingGeometryBuilder.hpp"
0017 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0018 #include "Acts/Material/HomogeneousVolumeMaterial.hpp"
0019 #include "Acts/Material/Interactions.hpp"
0020 #include "Acts/Material/Material.hpp"
0021 #include "Acts/Surfaces/CylinderSurface.hpp"
0022 #include "Acts/Surfaces/DiscSurface.hpp"
0023 #include "Acts/Surfaces/PlaneSurface.hpp"
0024 #include "Acts/Surfaces/RectangleBounds.hpp"
0025 #include "Acts/Surfaces/StrawSurface.hpp"
0026 #include "Acts/Utilities/Logger.hpp"
0027 #include "Acts/Utilities/MathHelpers.hpp"
0028 #include "Acts/Utilities/UnitVectors.hpp"
0029 #include "Acts/Utilities/detail/periodic.hpp"
0030 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0031 #include "ActsTests/CommonHelpers/PredefinedMaterials.hpp"
0032
0033 #include <utility>
0034
0035 inline std::shared_ptr<const Acts::TrackingGeometry> createDenseBlock(
0036 const Acts::GeometryContext& geoCtx) {
0037 using namespace Acts;
0038 using namespace ActsTests;
0039 using namespace UnitLiterals;
0040
0041 CuboidVolumeBuilder::VolumeConfig vConf;
0042 vConf.position = {0., 0., 0.};
0043 vConf.length = {4_m, 4_m, 4_m};
0044 vConf.volumeMaterial =
0045 std::make_shared<const HomogeneousVolumeMaterial>(makeBeryllium());
0046 CuboidVolumeBuilder::Config conf;
0047 conf.volumeCfg.push_back(vConf);
0048 conf.position = {0., 0., 0.};
0049 conf.length = {4_m, 4_m, 4_m};
0050 CuboidVolumeBuilder cvb(conf);
0051 TrackingGeometryBuilder::Config tgbCfg;
0052 tgbCfg.trackingVolumeBuilders.push_back(
0053 [=](const auto& context, const auto& inner, const auto&) {
0054 return cvb.trackingVolume(context, inner, nullptr);
0055 });
0056
0057 return TrackingGeometryBuilder(tgbCfg).trackingGeometry(geoCtx);
0058 }
0059
0060 inline std::tuple<std::shared_ptr<const Acts::TrackingGeometry>,
0061 std::vector<const Acts::Surface*>>
0062 createDenseTelescope(const Acts::GeometryContext& geoCtx,
0063 Acts::Material material, double thickness) {
0064 using namespace Acts;
0065 using namespace UnitLiterals;
0066
0067 CuboidVolumeBuilder::Config conf;
0068 conf.position = {0., 0., 0.};
0069 conf.length = {4_m, 2_m, 2_m};
0070
0071 {
0072 CuboidVolumeBuilder::VolumeConfig start;
0073 start.position = {-1_m, 0, 0};
0074 start.length = {1_m, 1_m, 1_m};
0075 start.name = "start";
0076
0077 conf.volumeCfg.push_back(start);
0078 }
0079
0080 if (thickness < 1_m) {
0081 CuboidVolumeBuilder::VolumeConfig gap;
0082 gap.position = {-0.25 * (1_m + thickness), 0, 0};
0083 gap.length = {0.5 * (1_m - thickness), 1_m, 1_m};
0084 gap.name = "gap1";
0085
0086 conf.volumeCfg.push_back(gap);
0087 }
0088
0089 {
0090 CuboidVolumeBuilder::VolumeConfig dense;
0091 dense.position = {0, 0, 0};
0092 dense.length = {thickness, 1_m, 1_m};
0093 dense.volumeMaterial =
0094 std::make_shared<const HomogeneousVolumeMaterial>(material);
0095 dense.name = "dense";
0096
0097 conf.volumeCfg.push_back(dense);
0098 }
0099
0100 if (thickness < 1_m) {
0101 CuboidVolumeBuilder::VolumeConfig gap;
0102 gap.position = {0.25 * (1_m + thickness), 0, 0};
0103 gap.length = {0.5 * (1_m - thickness), 1_m, 1_m};
0104 gap.name = "gap2";
0105
0106 conf.volumeCfg.push_back(gap);
0107 }
0108
0109 {
0110 CuboidVolumeBuilder::SurfaceConfig surface;
0111 surface.position = {1.5_m, 0, 0};
0112 surface.rotation =
0113 Eigen::AngleAxisd(std::numbers::pi / 2, Eigen::Vector3d::UnitY())
0114 .matrix();
0115 surface.rBounds = std::make_shared<RectangleBounds>(1_m, 1_m);
0116
0117 CuboidVolumeBuilder::LayerConfig layer;
0118 layer.surfaceCfg.push_back(surface);
0119
0120 CuboidVolumeBuilder::VolumeConfig end;
0121 end.position = {1_m, 0, 0};
0122 end.length = {1_m, 1_m, 1_m};
0123 end.layerCfg.push_back(layer);
0124 end.name = "end";
0125
0126 conf.volumeCfg.push_back(end);
0127 }
0128
0129 CuboidVolumeBuilder cvb(conf);
0130
0131 TrackingGeometryBuilder::Config tgbCfg;
0132 tgbCfg.trackingVolumeBuilders.push_back(
0133 [=](const auto& context, const auto& inner, const auto&) {
0134 return cvb.trackingVolume(context, inner, nullptr);
0135 });
0136 auto detector = TrackingGeometryBuilder(tgbCfg).trackingGeometry(geoCtx);
0137
0138 std::vector<const Surface*> surfaces;
0139 detector->visitSurfaces(
0140 [&](const Surface* surface) { surfaces.push_back(surface); });
0141
0142 return {std::move(detector), std::move(surfaces)};
0143 }
0144
0145
0146
0147
0148 inline Acts::BoundTrackParameters makeParametersCurvilinear(double phi,
0149 double theta,
0150 double absMom,
0151 double charge) {
0152 using namespace Acts;
0153 using namespace UnitLiterals;
0154
0155 Vector4 pos4 = Vector4::Zero();
0156 auto particleHypothesis = ParticleHypothesis::pionLike(std::abs(charge));
0157 return BoundTrackParameters::createCurvilinear(
0158 pos4, phi, theta, particleHypothesis.qOverP(absMom, charge), std::nullopt,
0159 particleHypothesis);
0160 }
0161
0162
0163 inline Acts::BoundTrackParameters makeParametersCurvilinearWithCovariance(
0164 double phi, double theta, double absMom, double charge) {
0165 using namespace Acts;
0166 using namespace UnitLiterals;
0167
0168 BoundVector stddev = BoundVector::Zero();
0169
0170 stddev[eBoundLoc0] = 15_um;
0171 stddev[eBoundLoc1] = 80_um;
0172 stddev[eBoundTime] = 20_ps;
0173 stddev[eBoundPhi] = 20_mrad;
0174 stddev[eBoundTheta] = 30_mrad;
0175 stddev[eBoundQOverP] = 1_e / 10_GeV;
0176 BoundMatrix corr = BoundMatrix::Identity();
0177 corr(eBoundLoc0, eBoundLoc1) = corr(eBoundLoc1, eBoundLoc0) = 0.125;
0178 corr(eBoundLoc0, eBoundPhi) = corr(eBoundPhi, eBoundLoc0) = 0.25;
0179 corr(eBoundLoc1, eBoundTheta) = corr(eBoundTheta, eBoundLoc1) = -0.25;
0180 corr(eBoundTime, eBoundQOverP) = corr(eBoundQOverP, eBoundTime) = 0.125;
0181 corr(eBoundPhi, eBoundTheta) = corr(eBoundTheta, eBoundPhi) = -0.25;
0182 corr(eBoundPhi, eBoundQOverP) = corr(eBoundPhi, eBoundQOverP) = -0.125;
0183 corr(eBoundTheta, eBoundQOverP) = corr(eBoundTheta, eBoundQOverP) = 0.5;
0184 BoundMatrix cov = stddev.asDiagonal() * corr * stddev.asDiagonal();
0185
0186 Vector4 pos4 = Vector4::Zero();
0187 auto particleHypothesis = ParticleHypothesis::pionLike(std::abs(charge));
0188 return BoundTrackParameters::createCurvilinear(
0189 pos4, phi, theta, particleHypothesis.qOverP(absMom, charge), cov,
0190 particleHypothesis);
0191 }
0192
0193
0194 inline Acts::BoundTrackParameters makeParametersCurvilinearNeutral(
0195 double phi, double theta, double absMom) {
0196 using namespace Acts;
0197 using namespace UnitLiterals;
0198
0199 Vector4 pos4 = Vector4::Zero();
0200 return BoundTrackParameters::createCurvilinear(
0201 pos4, phi, theta, 1 / absMom, std::nullopt, ParticleHypothesis::pion0());
0202 }
0203
0204
0205
0206
0207
0208
0209 inline void checkParametersConsistency(const Acts::BoundTrackParameters& cmp,
0210 const Acts::BoundTrackParameters& ref,
0211 const Acts::GeometryContext& geoCtx,
0212 double epsPos, double epsTime,
0213 double epsDir, double epsMom) {
0214 using namespace Acts;
0215
0216
0217 CHECK_CLOSE_ABS(cmp.template get<eBoundLoc0>(),
0218 ref.template get<eBoundLoc0>(), epsPos);
0219 CHECK_CLOSE_ABS(cmp.template get<eBoundLoc1>(),
0220 ref.template get<eBoundLoc1>(), epsPos);
0221 CHECK_CLOSE_ABS(cmp.template get<eBoundTime>(),
0222 ref.template get<eBoundTime>(), epsTime);
0223
0224 CHECK_SMALL(detail::radian_sym(cmp.template get<eBoundPhi>() -
0225 ref.template get<eBoundPhi>()),
0226 epsDir);
0227 CHECK_CLOSE_ABS(cmp.template get<eBoundTheta>(),
0228 ref.template get<eBoundTheta>(), epsDir);
0229 CHECK_CLOSE_ABS(cmp.template get<eBoundQOverP>(),
0230 ref.template get<eBoundQOverP>(), epsMom);
0231
0232 CHECK_CLOSE_ABS(cmp.position(geoCtx), ref.position(geoCtx), epsPos);
0233 CHECK_CLOSE_ABS(cmp.time(), ref.time(), epsTime);
0234 CHECK_CLOSE_ABS(cmp.direction(), ref.direction(), epsDir);
0235 CHECK_CLOSE_ABS(cmp.absoluteMomentum(), ref.absoluteMomentum(), epsMom);
0236
0237 BOOST_CHECK_EQUAL(cmp.charge(), ref.charge());
0238 }
0239
0240
0241
0242
0243 inline void checkCovarianceConsistency(const Acts::BoundTrackParameters& cmp,
0244 const Acts::BoundTrackParameters& ref,
0245 double relativeTolerance) {
0246
0247 if (cmp.covariance().has_value()) {
0248
0249 BOOST_CHECK(ref.covariance().has_value());
0250 }
0251 if (ref.covariance().has_value()) {
0252
0253 BOOST_CHECK(cmp.covariance().has_value());
0254 }
0255 if (cmp.covariance().has_value() && ref.covariance().has_value()) {
0256 CHECK_CLOSE_COVARIANCE(cmp.covariance().value(), ref.covariance().value(),
0257 relativeTolerance);
0258 }
0259 }
0260
0261
0262
0263
0264 inline Acts::Transform3 createCurvilinearTransform(
0265 const Acts::BoundTrackParameters& params,
0266 const Acts::GeometryContext& geoCtx) {
0267 using namespace Acts;
0268
0269 Vector3 unitW = params.direction();
0270 auto [unitU, unitV] = createCurvilinearUnitVectors(unitW);
0271
0272 RotationMatrix3 rotation = RotationMatrix3::Zero();
0273 rotation.col(0) = unitU;
0274 rotation.col(1) = unitV;
0275 rotation.col(2) = unitW;
0276 Translation3 offset(params.position(geoCtx));
0277 Transform3 toGlobal = offset * rotation;
0278
0279 return toGlobal;
0280 }
0281
0282
0283 struct ZCylinderSurfaceBuilder {
0284 std::shared_ptr<Acts::CylinderSurface> operator()(
0285 const Acts::BoundTrackParameters& params,
0286 const Acts::GeometryContext& geoCtx) {
0287 using namespace Acts;
0288
0289 auto radius = params.position(geoCtx).template head<2>().norm();
0290 auto halfz = std::numeric_limits<double>::max();
0291 return Surface::makeShared<CylinderSurface>(Transform3::Identity(), radius,
0292 halfz);
0293 }
0294 };
0295
0296
0297 struct DiscSurfaceBuilder {
0298 std::shared_ptr<Acts::DiscSurface> operator()(
0299 const Acts::BoundTrackParameters& params,
0300 const Acts::GeometryContext& geoCtx) {
0301 using namespace Acts;
0302 using namespace UnitLiterals;
0303
0304 auto cl = createCurvilinearTransform(params, geoCtx);
0305
0306
0307
0308
0309 Vector3 localOffset = Vector3::Zero();
0310 localOffset[ePos0] = 1_cm;
0311 localOffset[ePos1] = -1_cm;
0312 Vector3 globalOriginDelta = cl.linear() * localOffset;
0313 cl.pretranslate(globalOriginDelta);
0314
0315 return Surface::makeShared<DiscSurface>(cl);
0316 }
0317 };
0318
0319
0320 struct PlaneSurfaceBuilder {
0321 std::shared_ptr<Acts::PlaneSurface> operator()(
0322 const Acts::BoundTrackParameters& params,
0323 const Acts::GeometryContext& geoCtx) {
0324 using namespace Acts;
0325
0326 return Surface::makeShared<PlaneSurface>(
0327 createCurvilinearTransform(params, geoCtx));
0328 }
0329 };
0330
0331
0332 struct ZStrawSurfaceBuilder {
0333 std::shared_ptr<Acts::StrawSurface> operator()(
0334 const Acts::BoundTrackParameters& params,
0335 const Acts::GeometryContext& geoCtx) {
0336 using namespace Acts;
0337
0338 return Surface::makeShared<StrawSurface>(
0339 Transform3(Translation3(params.position(geoCtx))));
0340 }
0341 };
0342
0343
0344
0345
0346
0347
0348 template <typename propagator_t,
0349 typename options_t = typename propagator_t::template Options<>>
0350 inline std::pair<Acts::BoundTrackParameters, double> transportFreely(
0351 const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0352 const Acts::MagneticFieldContext& magCtx,
0353 const Acts::BoundTrackParameters& initialParams, double pathLength) {
0354 using namespace Acts;
0355 using namespace UnitLiterals;
0356
0357
0358 options_t options(geoCtx, magCtx);
0359 options.direction = Direction::fromScalar(pathLength);
0360 options.pathLimit = pathLength;
0361
0362 auto result = propagator.propagate(initialParams, options);
0363 BOOST_CHECK(result.ok());
0364 BOOST_CHECK(result.value().endParameters);
0365
0366 return {*result.value().endParameters, result.value().pathLength};
0367 }
0368
0369
0370 template <typename propagator_t,
0371 typename options_t = typename propagator_t::template Options<>>
0372 inline std::pair<Acts::BoundTrackParameters, double> transportToSurface(
0373 const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0374 const Acts::MagneticFieldContext& magCtx,
0375 const Acts::BoundTrackParameters& initialParams,
0376 const Acts::Surface& targetSurface, double pathLimit) {
0377 using namespace Acts;
0378 using namespace UnitLiterals;
0379
0380
0381 options_t options(geoCtx, magCtx);
0382 options.direction = Direction::Forward();
0383 options.pathLimit = pathLimit;
0384
0385 auto result = propagator.propagate(initialParams, targetSurface, options);
0386 BOOST_CHECK(result.ok());
0387 BOOST_CHECK(result.value().endParameters);
0388
0389 return {*result.value().endParameters, result.value().pathLength};
0390 }
0391
0392
0393
0394
0395
0396
0397 template <typename propagator_t,
0398 typename options_t = typename propagator_t::template Options<>>
0399 inline void runForwardBackwardTest(
0400 const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0401 const Acts::MagneticFieldContext& magCtx,
0402 const Acts::BoundTrackParameters& initialParams, double pathLength,
0403 double epsPos, double epsTime, double epsDir, double epsMom) {
0404
0405 auto [fwdParams, fwdPathLength] = transportFreely<propagator_t, options_t>(
0406 propagator, geoCtx, magCtx, initialParams, pathLength);
0407 CHECK_CLOSE_ABS(fwdPathLength, pathLength, epsPos);
0408
0409 auto [bwdParams, bwdPathLength] = transportFreely<propagator_t, options_t>(
0410 propagator, geoCtx, magCtx, fwdParams, -pathLength);
0411 CHECK_CLOSE_ABS(bwdPathLength, -pathLength, epsPos);
0412
0413 checkParametersConsistency(initialParams, bwdParams, geoCtx, epsPos, epsTime,
0414 epsDir, epsMom);
0415 }
0416
0417
0418
0419
0420
0421 template <typename propagator_t, typename surface_builder_t,
0422 typename options_t = typename propagator_t::template Options<>>
0423 inline void runToSurfaceTest(const propagator_t& propagator,
0424 const Acts::GeometryContext& geoCtx,
0425 const Acts::MagneticFieldContext& magCtx,
0426 const Acts::BoundTrackParameters& initialParams,
0427 double pathLength,
0428 surface_builder_t&& buildTargetSurface,
0429 double epsPos, double epsTime, double epsDir,
0430 double epsMom) {
0431
0432 auto [freeParams, freePathLength] = transportFreely<propagator_t, options_t>(
0433 propagator, geoCtx, magCtx, initialParams, pathLength);
0434 CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
0435
0436 auto surface = buildTargetSurface(freeParams, geoCtx);
0437 BOOST_CHECK(surface);
0438
0439
0440
0441 auto [surfParams, surfPathLength] =
0442 transportToSurface<propagator_t, options_t>(propagator, geoCtx, magCtx,
0443 initialParams, *surface,
0444 1.5 * pathLength);
0445 CHECK_CLOSE_ABS(surfPathLength, pathLength, epsPos);
0446
0447
0448 CHECK_CLOSE_ABS(surfParams.position(geoCtx), freeParams.position(geoCtx),
0449 epsPos);
0450 CHECK_CLOSE_ABS(surfParams.time(), freeParams.time(), epsTime);
0451 CHECK_CLOSE_ABS(surfParams.direction(), freeParams.direction(), epsDir);
0452 CHECK_CLOSE_ABS(surfParams.absoluteMomentum(), freeParams.absoluteMomentum(),
0453 epsMom);
0454 CHECK_CLOSE_ABS(surfPathLength, freePathLength, epsPos);
0455 }
0456
0457
0458
0459
0460
0461 template <typename cmp_propagator_t, typename ref_propagator_t>
0462 inline void runForwardComparisonTest(
0463 const cmp_propagator_t& cmpPropagator,
0464 const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
0465 const Acts::MagneticFieldContext& magCtx,
0466 const Acts::BoundTrackParameters& initialParams, double pathLength,
0467 double epsPos, double epsTime, double epsDir, double epsMom,
0468 double tolCov) {
0469
0470 auto [cmpParams, cmpPath] = transportFreely<cmp_propagator_t>(
0471 cmpPropagator, geoCtx, magCtx, initialParams, pathLength);
0472 auto [refParams, refPath] = transportFreely<ref_propagator_t>(
0473 refPropagator, geoCtx, magCtx, initialParams, pathLength);
0474
0475 checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsTime,
0476 epsDir, epsMom);
0477 checkCovarianceConsistency(cmpParams, refParams, tolCov);
0478 CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
0479 CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
0480 CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
0481 }
0482
0483
0484
0485
0486
0487 template <typename cmp_propagator_t, typename ref_propagator_t,
0488 typename surface_builder_t>
0489 inline void runToSurfaceComparisonTest(
0490 const cmp_propagator_t& cmpPropagator,
0491 const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
0492 const Acts::MagneticFieldContext& magCtx,
0493 const Acts::BoundTrackParameters& initialParams, double pathLength,
0494 surface_builder_t&& buildTargetSurface, double epsPos, double epsTime,
0495 double epsDir, double epsMom, double tolCov) {
0496
0497 auto [freeParams, freePathLength] = transportFreely<ref_propagator_t>(
0498 refPropagator, geoCtx, magCtx, initialParams, pathLength);
0499 CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
0500
0501
0502 auto surface = buildTargetSurface(freeParams, geoCtx);
0503 BOOST_CHECK(surface);
0504
0505
0506
0507 auto [cmpParams, cmpPath] = transportToSurface<cmp_propagator_t>(
0508 cmpPropagator, geoCtx, magCtx, initialParams, *surface, 1.5 * pathLength);
0509 auto [refParams, refPath] = transportToSurface<ref_propagator_t>(
0510 refPropagator, geoCtx, magCtx, initialParams, *surface, 1.5 * pathLength);
0511
0512 checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsTime,
0513 epsDir, epsMom);
0514 checkCovarianceConsistency(cmpParams, refParams, tolCov);
0515 CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
0516 CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
0517 CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
0518 }
0519
0520 template <typename propagator_t>
0521 inline void runDenseForwardTest(
0522 const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0523 const Acts::MagneticFieldContext& magCtx, double p, double q,
0524 const Acts::Surface& targetSurface, const Acts::Material& material,
0525 double thickness, const Acts::Logger& logger = Acts::getDummyLogger()) {
0526 using namespace Acts;
0527 using namespace UnitLiterals;
0528
0529 const auto particleHypothesis = ParticleHypothesis::muon();
0530 const float mass = particleHypothesis.mass();
0531 const float absQ = std::abs(q);
0532 const double initialQOverP = particleHypothesis.qOverP(p, q);
0533 const auto initialParams = BoundTrackParameters::createCurvilinear(
0534 Vector4(-1.5_m, 0, 0, 0), Vector3::UnitX(), initialQOverP,
0535 BoundVector::Constant(1e-16).asDiagonal(), particleHypothesis);
0536
0537 typename propagator_t::template Options<> options(geoCtx, magCtx);
0538 options.maxSteps = 10000;
0539 options.stepping.maxStepSize = 100_mm;
0540 options.stepping.dense.meanEnergyLoss = true;
0541 options.stepping.dense.momentumCutOff = 1_MeV;
0542
0543 const auto result =
0544 propagator.propagate(initialParams, targetSurface, options);
0545
0546 BOOST_CHECK(result.ok());
0547 CHECK_CLOSE_REL(3_m, result->pathLength, 1e-6);
0548 BOOST_CHECK(result->endParameters);
0549
0550 BoundTrackParameters endParams = result->endParameters.value();
0551
0552 BOOST_CHECK(endParams.covariance());
0553 CHECK_CLOSE_ABS(initialParams.position(geoCtx) + Acts::Vector3(3_m, 0, 0),
0554 endParams.position(geoCtx), 1e-6);
0555 CHECK_CLOSE_ABS(initialParams.direction(), endParams.direction(), 1e-6);
0556
0557 const auto& cov = endParams.covariance().value();
0558
0559 const double endQOverP = endParams.qOverP();
0560 const double endP = endParams.absoluteMomentum();
0561 const double endEloss =
0562 Acts::fastHypot(p, mass) - Acts::fastHypot(endP, mass);
0563 const double endErrX = std::sqrt(cov(eBoundLoc0, eBoundLoc0));
0564 const double endErrY = std::sqrt(cov(eBoundLoc1, eBoundLoc1));
0565 const double endErrQOverP = std::sqrt(cov(eBoundQOverP, eBoundQOverP));
0566 const double endErrP =
0567 (absQ / std::pow(endParams.qOverP(), 2)) * endErrQOverP;
0568 const double endErrE = (p / Acts::fastHypot(p, mass)) * endErrP;
0569 const double endErrTheta = std::sqrt(cov(eBoundTheta, eBoundTheta));
0570 const double endErrPhi = std::sqrt(cov(eBoundPhi, eBoundPhi));
0571
0572 ACTS_VERBOSE("input q/p = " << initialQOverP);
0573 ACTS_VERBOSE("input p = " << p);
0574 ACTS_VERBOSE("output q/p = " << endQOverP);
0575 ACTS_VERBOSE("output p = " << endP);
0576 ACTS_VERBOSE("output t = " << endParams.time());
0577 ACTS_VERBOSE("output eloss = " << endEloss);
0578 ACTS_VERBOSE("output err x = " << endErrX);
0579 ACTS_VERBOSE("output err y = " << endErrY);
0580 ACTS_VERBOSE("output err q/p = " << endErrQOverP);
0581 ACTS_VERBOSE("output err p = " << endErrP);
0582 ACTS_VERBOSE("output err E = " << endErrE);
0583 ACTS_VERBOSE("output err theta = " << endErrTheta);
0584 ACTS_VERBOSE("output err phi = " << endErrPhi);
0585
0586 BOOST_CHECK_CLOSE(endErrX, endErrY, 1e-6);
0587 BOOST_CHECK_CLOSE(endErrTheta, endErrPhi, 1e-6);
0588
0589 const float elossInitial = computeEnergyLossMean(
0590 MaterialSlab(material, thickness), particleHypothesis.absolutePdg(), mass,
0591 initialQOverP, absQ);
0592 const float elossFinal = computeEnergyLossMean(
0593 MaterialSlab(material, thickness), particleHypothesis.absolutePdg(), mass,
0594 endQOverP, absQ);
0595
0596 ACTS_VERBOSE("ref eloss initial = " << elossInitial);
0597 ACTS_VERBOSE("ref eloss final = " << elossFinal);
0598
0599
0600
0601 BOOST_CHECK_LT(endEloss, elossInitial);
0602 BOOST_CHECK_GT(endEloss, elossFinal);
0603
0604 const float theta0Initial = computeMultipleScatteringTheta0(
0605 MaterialSlab(material, thickness), particleHypothesis.absolutePdg(), mass,
0606 initialQOverP, absQ);
0607 const float theta0Final = computeMultipleScatteringTheta0(
0608 MaterialSlab(material, thickness), particleHypothesis.absolutePdg(), mass,
0609 endQOverP, absQ);
0610
0611 ACTS_VERBOSE("ref theta0 initial = " << theta0Initial);
0612 ACTS_VERBOSE("ref theta0 final = " << theta0Final);
0613
0614
0615 BOOST_CHECK_GE(endErrTheta, theta0Initial);
0616 BOOST_CHECK_LT(endErrTheta, theta0Final);
0617 CHECK_CLOSE_REL(endErrTheta, 0.5 * (theta0Initial + theta0Final), 0.1);
0618
0619
0620
0621 const auto estimateVarX = [&](double qOverP, std::size_t substeps) {
0622 double previousTheta0 = 0;
0623 double varAngle = 0;
0624 double varPosition = 0;
0625 double covAnglePosition = 0;
0626
0627 const auto step = [&](double substep, double theta0) {
0628 double deltaVarTheta = square(theta0) - square(previousTheta0);
0629 double deltaVarPos = varAngle * square(substep) +
0630 2 * covAnglePosition * substep +
0631 deltaVarTheta * (square(substep) / 3);
0632 double deltaCovAnglePosition =
0633 varAngle * substep + deltaVarTheta * substep / 2;
0634 previousTheta0 = theta0;
0635 varAngle += deltaVarTheta;
0636 varPosition += deltaVarPos;
0637 covAnglePosition += deltaCovAnglePosition;
0638 };
0639
0640
0641 for (std::size_t i = 0; i < substeps; ++i) {
0642 const float theta0 = computeMultipleScatteringTheta0(
0643 MaterialSlab(material, (i + 1) * thickness / substeps),
0644 particleHypothesis.absolutePdg(), mass, qOverP, absQ);
0645 step(thickness / substeps, theta0);
0646 }
0647
0648
0649 step(1_m, previousTheta0);
0650
0651 return varPosition;
0652 };
0653
0654
0655 const double lowerBoundVarX = estimateVarX(initialQOverP, 10);
0656
0657 const double upperBoundVarX = estimateVarX(endQOverP, 10);
0658
0659 const double lowerBoundSigmaX = std::sqrt(lowerBoundVarX);
0660 const double upperBoundSigmaX = std::sqrt(upperBoundVarX);
0661
0662 ACTS_VERBOSE("ref lower bound sigma x = " << lowerBoundSigmaX);
0663 ACTS_VERBOSE("ref upper bound sigma x = " << upperBoundSigmaX);
0664
0665 BOOST_CHECK_GT(endErrX, lowerBoundSigmaX);
0666 BOOST_CHECK_LT(endErrX, upperBoundSigmaX);
0667 CHECK_CLOSE_REL(endErrX, 0.5 * (lowerBoundSigmaX + upperBoundSigmaX), 0.2);
0668
0669 const float qOverPSigma = computeEnergyLossLandauSigmaQOverP(
0670 MaterialSlab(material, thickness), mass, initialQOverP, absQ);
0671
0672 ACTS_VERBOSE("ref sigma q/p = " << qOverPSigma);
0673
0674 CHECK_CLOSE_REL(endErrQOverP, qOverPSigma, 1e-4);
0675 }