File indexing completed on 2026-05-27 07:24:03
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "./codegen/update_rk_transport_jacobian.hpp"
0013 #include "detray/definitions/algebra.hpp"
0014 #include "detray/definitions/detail/qualifiers.hpp"
0015 #include "detray/definitions/units.hpp"
0016 #include "detray/material/interaction.hpp"
0017 #include "detray/material/material.hpp"
0018 #include "detray/navigation/policies.hpp"
0019 #include "detray/propagator/base_stepper.hpp"
0020 #include "detray/propagator/transport_jacobian.hpp"
0021 #include "detray/tracks/tracks.hpp"
0022
0023 namespace detray {
0024 enum class rk_stepper_flags : std::uint32_t {
0025 e_allow_covariance_transport = 1,
0026 e_allow_field_gradient = 2,
0027 };
0028
0029
0030
0031
0032
0033
0034 template <typename magnetic_field_t, concepts::algebra algebra_t,
0035 typename constraint_t = unconstrained_step<dscalar<algebra_t>>,
0036 typename policy_t = stepper_rk_policy<dscalar<algebra_t>>,
0037 typename inspector_t = stepping::void_inspector,
0038 std::uint32_t flags_v =
0039 (static_cast<std::uint32_t>(
0040 rk_stepper_flags::e_allow_covariance_transport) |
0041 static_cast<std::uint32_t>(
0042 rk_stepper_flags::e_allow_field_gradient))>
0043 class rk_stepper final
0044 : public base_stepper<algebra_t, constraint_t, policy_t, inspector_t> {
0045 using base_type =
0046 base_stepper<algebra_t, constraint_t, policy_t, inspector_t>;
0047
0048 public:
0049 static constexpr bool uses_gradient = static_cast<bool>(
0050 flags_v &
0051 static_cast<std::uint32_t>(rk_stepper_flags::e_allow_field_gradient));
0052
0053 using algebra_type = algebra_t;
0054 using scalar_type = dscalar<algebra_t>;
0055 using point3_type = dpoint3D<algebra_t>;
0056 using vector3_type = dvector3D<algebra_t>;
0057 using transform3_type = dtransform3D<algebra_t>;
0058 using free_track_parameters_type =
0059 typename base_type::free_track_parameters_type;
0060 using bound_track_parameters_type =
0061 typename base_type::bound_track_parameters_type;
0062 using magnetic_field_type = magnetic_field_t;
0063 template <std::size_t ROWS, std::size_t COLS>
0064 using matrix_type = dmatrix<algebra_t, ROWS, COLS>;
0065 using transport_jacobian_type = std::conditional_t<
0066 uses_gradient,
0067 detail::transport_jacobian_matrix_with_gradient<algebra_type>,
0068 detail::transport_jacobian_matrix_without_gradient<algebra_type>>;
0069
0070 rk_stepper() = default;
0071
0072 struct intermediate_state {
0073 vector3_type b_first{0.f, 0.f, 0.f};
0074 vector3_type b_middle{0.f, 0.f, 0.f};
0075 vector3_type b_last{0.f, 0.f, 0.f};
0076
0077 darray<vector3_type, 4u> t;
0078
0079 darray<scalar_type, 4u> qop;
0080
0081 darray<vector3_type, 4u> dtds;
0082
0083 darray<scalar_type, 4u> dqopds;
0084 };
0085
0086 struct state : public base_type::template state<transport_jacobian_type> {
0087 static constexpr const stepping::id id = stepping::id::e_rk;
0088
0089 using base_state =
0090 typename base_type::template state<transport_jacobian_type>;
0091
0092 DETRAY_HOST_DEVICE
0093 state(const free_track_parameters_type& t,
0094 const magnetic_field_t& mag_field)
0095 : base_state(t), m_magnetic_field(mag_field) {}
0096
0097 template <typename detector_t>
0098 DETRAY_HOST_DEVICE state(const bound_track_parameters_type& bound_params,
0099 const magnetic_field_t& mag_field,
0100 const detector_t& det,
0101 const typename detector_t::geometry_context& ctx)
0102 : base_state(bound_params, det, ctx), m_magnetic_field(mag_field) {}
0103
0104
0105 DETRAY_HOST_DEVICE
0106 const magnetic_field_type& field() const { return m_magnetic_field; }
0107
0108
0109 DETRAY_HOST_DEVICE
0110 inline void set_next_step_size(const scalar_type step) {
0111 assert(math::fabs(step) >= 1e-4f * unit<scalar_type>::mm);
0112 m_next_step_size = step;
0113 }
0114
0115
0116 DETRAY_HOST_DEVICE
0117 inline scalar_type next_step_size() const { return m_next_step_size; }
0118
0119
0120 DETRAY_HOST_DEVICE
0121 vector3_type dtds() const {
0122
0123 if (this->path_length() == 0.f) [[unlikely]] {
0124 const point3_type pos = (*this)().pos();
0125
0126 const auto bvec_tmp = this->field().at(pos[0], pos[1], pos[2]);
0127 vector3_type bvec;
0128 bvec[0u] = bvec_tmp[0u];
0129 bvec[1u] = bvec_tmp[1u];
0130 bvec[2u] = bvec_tmp[2u];
0131
0132 DETRAY_DEBUG_HOST(
0133 "--> dtds: " << (*this)().qop() *
0134 vector::cross((*this)().dir(), bvec));
0135
0136 return (*this)().qop() * vector::cross((*this)().dir(), bvec);
0137 }
0138
0139 return m_dtds_3;
0140 }
0141
0142
0143 DETRAY_HOST_DEVICE
0144 void dtds(const vector3_type& v) { m_dtds_3 = v; }
0145
0146
0147 DETRAY_HOST_DEVICE
0148 scalar_type dqopds(const material<scalar_type>* vol_mat_ptr) const {
0149
0150 if (this->path_length() == 0.f) [[unlikely]] {
0151 return this->dqopds((*this)().qop(), vol_mat_ptr);
0152 }
0153
0154 return m_dqopds_3;
0155 }
0156
0157 DETRAY_HOST_DEVICE
0158 scalar_type dqopds(const scalar_type qop,
0159 const material<scalar_type>* vol_mat_ptr) const {
0160
0161 if (vol_mat_ptr == nullptr) {
0162 return 0.f;
0163 }
0164
0165 assert(qop != 0.f);
0166 assert(std::isfinite(qop));
0167 const scalar_type q = this->particle_hypothesis().charge();
0168 const scalar_type p = q / qop;
0169 const scalar_type mass = this->particle_hypothesis().mass();
0170 const scalar_type E = math::sqrt(p * p + mass * mass);
0171
0172
0173 const scalar_type stopping_power =
0174 interaction<scalar_type>().compute_stopping_power(
0175 *vol_mat_ptr, this->particle_hypothesis(), {mass, qop, q});
0176
0177
0178 assert(p >= 0.f);
0179 assert(q != 0.f);
0180
0181 DETRAY_DEBUG_HOST_DEVICE("--> dqopds: %f",
0182 qop * qop * qop * E * stopping_power / (q * q));
0183
0184
0185
0186 return qop * qop * qop * E * stopping_power / (q * q);
0187 }
0188
0189
0190 DETRAY_HOST_DEVICE void dqopds(const scalar_type& s) { m_dqopds_3 = s; }
0191
0192
0193 DETRAY_HOST_DEVICE
0194 scalar_type d2qopdsdqop(const scalar_type qop,
0195 const material<scalar_type>* vol_mat_ptr) const {
0196 if (vol_mat_ptr == nullptr) {
0197 return 0.f;
0198 }
0199
0200 const scalar_type q = this->particle_hypothesis().charge();
0201 const scalar_type p = q / qop;
0202 const scalar_type p2 = p * p;
0203
0204 const auto& mass = this->particle_hypothesis().mass();
0205 const scalar_type E2 = p2 + mass * mass;
0206
0207
0208 interaction<scalar_type> I;
0209
0210
0211 const detail::relativistic_quantities<scalar_type> rq(mass, qop, q);
0212 const scalar_type g =
0213 -1.f * I.compute_stopping_power(*vol_mat_ptr,
0214 this->particle_hypothesis(), rq);
0215
0216
0217 const scalar_type dgdqop =
0218 -1.f * I.derive_stopping_power(*vol_mat_ptr,
0219 this->particle_hypothesis(), rq);
0220
0221
0222 const scalar_type dqopds = this->dqopds(qop, vol_mat_ptr);
0223
0224
0225
0226 assert(E2 != 0.f);
0227 assert(g != 0.f);
0228 return dqopds * (1.f / qop * (3.f - p2 / E2) + 1.f / g * dgdqop);
0229 }
0230
0231
0232 template <typename... Args>
0233 DETRAY_HOST_DEVICE void run_inspector(
0234 [[maybe_unused]] const stepping::config& cfg,
0235 [[maybe_unused]] const char* message,
0236 [[maybe_unused]] const scalar_type dist,
0237 [[maybe_unused]] Args&&... args) {
0238 if constexpr (!std::is_same_v<inspector_t, stepping::void_inspector>) {
0239 this->inspector()(*this, cfg, message, dist,
0240 std::forward<Args>(args)...);
0241 }
0242
0243 if constexpr (sizeof...(Args) > 0u) {
0244 DETRAY_DEBUG_HOST("" << message << "\n"
0245 << detray::stepping::print_state(
0246 *this, std::forward<Args>(args)...));
0247 }
0248 }
0249
0250 private:
0251 vector3_type m_dtds_3;
0252 scalar_type m_dqopds_3;
0253
0254
0255 scalar_type m_next_step_size{0.f};
0256
0257
0258 const magnetic_field_type& m_magnetic_field;
0259 };
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269 DETRAY_HOST_DEVICE bool step(
0270 const scalar_type dist_to_next, state& stepping,
0271 const stepping::config& cfg, bool do_reset,
0272 const material<scalar_type>* vol_mat_ptr = nullptr) const {
0273 DETRAY_DEBUG_HOST("Before: " << stepping());
0274
0275
0276 if (math::fabs(dist_to_next) < 1.f * unit<float>::um) [[unlikely]] {
0277 DETRAY_VERBOSE_HOST_DEVICE("Zero stepsize...");
0278
0279
0280 if (math::fabs(stepping.next_step_size()) < cfg.min_stepsize) {
0281 stepping.set_next_step_size(math::copysign(
0282 static_cast<scalar_type>(cfg.min_stepsize), dist_to_next));
0283 }
0284 DETRAY_DEBUG_HOST_DEVICE("Setting next stepsize: %f mm",
0285 stepping.next_step_size());
0286
0287 stepping.run_inspector(cfg, "Step skipped (Overlap): ", dist_to_next);
0288
0289 DETRAY_DEBUG_HOST("After: " << stepping());
0290 return true;
0291 }
0292
0293 if (!(do_reset ||
0294 math::fabs(stepping.next_step_size()) >= cfg.min_stepsize)) {
0295 DETRAY_INFO_HOST("Next stepsize: " << stepping.next_step_size() << ", "
0296 << cfg.min_stepsize);
0297 }
0298
0299
0300 assert(math::fabs(dist_to_next) != 0.f);
0301 assert(do_reset ||
0302 math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
0303 assert(!stepping().is_invalid());
0304
0305
0306 auto& magnetic_field = stepping.field();
0307
0308 if (do_reset) {
0309 stepping.set_step_size(dist_to_next);
0310 } else if (stepping.next_step_size() > 0) {
0311 assert(math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
0312 stepping.set_step_size(
0313 math::min(stepping.next_step_size(), dist_to_next));
0314 } else {
0315 assert(math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
0316 stepping.set_step_size(
0317 math::max(stepping.next_step_size(), dist_to_next));
0318 }
0319
0320 DETRAY_VERBOSE_HOST_DEVICE("Distance to nex: %f mm", dist_to_next);
0321 DETRAY_VERBOSE_HOST_DEVICE("Initial stepsize: %f mm", stepping.step_size());
0322
0323
0324 const scalar_type min_stepsize{
0325 math::min(math::fabs(stepping.step_size()),
0326 static_cast<scalar_type>(cfg.min_stepsize))};
0327 const point3_type pos = stepping().pos();
0328
0329 intermediate_state sd{};
0330
0331
0332 auto bvec = magnetic_field.at(pos[0], pos[1], pos[2]);
0333 assert(math::isfinite(bvec[0]));
0334 assert(math::isfinite(bvec[1]));
0335 assert(math::isfinite(bvec[2]));
0336 sd.b_first[0] = bvec[0];
0337 sd.b_first[1] = bvec[1];
0338 sd.b_first[2] = bvec[2];
0339
0340 DETRAY_DEBUG_HOST_DEVICE("First stage:");
0341 DETRAY_DEBUG_HOST_DEVICE("-> B-field: [%f, %f, %f]", bvec[0], bvec[1],
0342 bvec[2]);
0343
0344
0345
0346 detray::tie(sd.dqopds[0u], sd.qop[0u]) =
0347 evaluate_dqopds(stepping, 0u, 0.f, 0.f, vol_mat_ptr, cfg);
0348 detray::tie(sd.dtds[0u], sd.t[0u]) = evaluate_dtds(
0349 stepping, sd.b_first, 0u, 0.f, vector3_type{0.f, 0.f, 0.f}, sd.qop[0u]);
0350
0351
0352 const auto estimate_error = [&](const scalar_type& h) {
0353 assert(h != 0);
0354
0355 const scalar_type h2{h * h};
0356 const scalar_type half_h{h * 0.5f};
0357
0358
0359
0360
0361 const point3_type pos1 =
0362 pos + half_h * sd.t[0u] + h2 * 0.125f * sd.dtds[0u];
0363 bvec = magnetic_field.at(pos1[0], pos1[1], pos1[2]);
0364 assert(math::isfinite(bvec[0]));
0365 assert(math::isfinite(bvec[1]));
0366 assert(math::isfinite(bvec[2]));
0367 sd.b_middle[0] = bvec[0];
0368 sd.b_middle[1] = bvec[1];
0369 sd.b_middle[2] = bvec[2];
0370 DETRAY_DEBUG_HOST_DEVICE("Second stage:");
0371 DETRAY_DEBUG_HOST_DEVICE("-> B-field: [%f, %f, %f]", bvec[0], bvec[1],
0372 bvec[2]);
0373
0374 detray::tie(sd.dqopds[1u], sd.qop[1u]) = evaluate_dqopds(
0375 stepping, 1u, half_h, sd.dqopds[0u], vol_mat_ptr, cfg);
0376 detray::tie(sd.dtds[1u], sd.t[1u]) = evaluate_dtds(
0377 stepping, sd.b_middle, 1u, half_h, sd.dtds[0u], sd.qop[1u]);
0378
0379
0380
0381
0382
0383 detray::tie(sd.dqopds[2u], sd.qop[2u]) = evaluate_dqopds(
0384 stepping, 2u, half_h, sd.dqopds[1u], vol_mat_ptr, cfg);
0385 detray::tie(sd.dtds[2u], sd.t[2u]) = evaluate_dtds(
0386 stepping, sd.b_middle, 2u, half_h, sd.dtds[1u], sd.qop[2u]);
0387
0388
0389
0390
0391 const point3_type pos2 = pos + h * sd.t[0u] + h2 * 0.5f * sd.dtds[2u];
0392 bvec = magnetic_field.at(pos2[0], pos2[1], pos2[2]);
0393 assert(math::isfinite(bvec[0]));
0394 assert(math::isfinite(bvec[1]));
0395 assert(math::isfinite(bvec[2]));
0396 sd.b_last[0] = bvec[0];
0397 sd.b_last[1] = bvec[1];
0398 sd.b_last[2] = bvec[2];
0399
0400 DETRAY_DEBUG_HOST_DEVICE("Third stage:");
0401 DETRAY_DEBUG_HOST_DEVICE("-> B-field: [%f, %f, %f]", bvec[0], bvec[1],
0402 bvec[2]);
0403
0404 detray::tie(sd.dqopds[3u], sd.qop[3u]) =
0405 evaluate_dqopds(stepping, 3u, h, sd.dqopds[2u], vol_mat_ptr, cfg);
0406 detray::tie(sd.dtds[3u], sd.t[3u]) =
0407 evaluate_dtds(stepping, sd.b_last, 3u, h, sd.dtds[2u], sd.qop[3u]);
0408
0409
0410
0411 constexpr auto one_sixth{static_cast<scalar_type>(1. / 6.)};
0412 const vector3_type err_vec =
0413 one_sixth * h2 *
0414 (sd.dtds[0u] - sd.dtds[1u] - sd.dtds[2u] + sd.dtds[3u]);
0415
0416 DETRAY_DEBUG_HOST("-> Integration error vector: " << err_vec);
0417
0418 return vector::norm(err_vec);
0419 };
0420
0421
0422
0423 const auto step_size_scaling =
0424 [&cfg](const scalar_type& err) -> scalar_type {
0425 assert(err != 0.f);
0426 return static_cast<scalar_type>(
0427 math::min(math::max(math::sqrt(math::sqrt(cfg.rk_error_tol / err)),
0428 static_cast<scalar_type>(0.25)),
0429 static_cast<scalar_type>(4.)));
0430 };
0431
0432 scalar_type error{1e20f};
0433
0434
0435
0436 const auto n_trials{cfg.max_rk_updates};
0437 for (unsigned int i = 0u; i < n_trials; i++) {
0438 stepping.count_trials();
0439
0440 error = math::max(estimate_error(stepping.step_size()),
0441 static_cast<scalar_type>(1e-20));
0442
0443 DETRAY_DEBUG_HOST_DEVICE("-> Integration error: %f", error);
0444 assert(math::isfinite(error));
0445
0446
0447
0448 if (error <= 4.f * cfg.rk_error_tol) {
0449 break;
0450 }
0451
0452
0453 else {
0454 stepping.set_step_size(stepping.step_size() * step_size_scaling(error));
0455
0456
0457 stepping.run_inspector(cfg, "Adjust stepsize: ", dist_to_next, i,
0458 step_size_scaling(error));
0459 }
0460 }
0461
0462 stepping.dtds(sd.dtds[3u]);
0463 stepping.dqopds(sd.dqopds[3u]);
0464
0465
0466 if (const scalar_type max_step =
0467 stepping.constraints().template size<>(stepping.direction());
0468 math::fabs(stepping.step_size()) > math::fabs(max_step)) {
0469
0470 stepping.run_inspector(cfg, "Before constraint: ", dist_to_next);
0471
0472 stepping.set_step_size(max_step);
0473 }
0474
0475
0476 if (math::fabs(stepping.step_size()) < min_stepsize) {
0477 stepping.set_step_size(
0478 math::copysign(min_stepsize, stepping.step_size()));
0479 }
0480
0481 DETRAY_VERBOSE_HOST_DEVICE("Take step: %f mm", stepping.step_size());
0482
0483
0484 stepping.set_next_step_size(stepping.step_size() *
0485 step_size_scaling(error));
0486
0487
0488 if (math::fabs(stepping.next_step_size()) < cfg.min_stepsize) {
0489 stepping.set_next_step_size(
0490 math::copysign(static_cast<scalar_type>(cfg.min_stepsize),
0491 stepping.next_step_size()));
0492 }
0493
0494 DETRAY_DEBUG_HOST_DEVICE("Estimated next stepsize: %f mm",
0495 stepping.next_step_size());
0496
0497 assert(math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
0498 assert(math::fabs(stepping.step_size()) >= cfg.min_stepsize);
0499
0500
0501 advance_track(stepping, sd, vol_mat_ptr);
0502 assert(!stepping().is_invalid());
0503
0504
0505 if constexpr ((flags_v & static_cast<std::uint32_t>(
0506 detray::rk_stepper_flags::
0507 e_allow_covariance_transport)) != 0u) {
0508 if (cfg.do_covariance_transport) {
0509 advance_jacobian(stepping, cfg, sd, vol_mat_ptr);
0510 }
0511 } else {
0512 assert(!cfg.do_covariance_transport);
0513 }
0514
0515
0516 stepping.run_inspector(cfg, "Step complete: ", dist_to_next);
0517
0518 DETRAY_DEBUG_HOST("After: " << stepping());
0519 return true;
0520 }
0521
0522
0523 DETRAY_HOST_DEVICE
0524 detray::pair<scalar_type, scalar_type> evaluate_dqopds(
0525 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
0526 inspector_t, flags_v>::state& stepping,
0527 const std::size_t i, const scalar_type h, const scalar_type dqopds_prev,
0528 const material<scalar_type>* vol_mat_ptr,
0529 const detray::stepping::config& cfg) const {
0530 const auto& track = stepping();
0531
0532 if (vol_mat_ptr == nullptr) {
0533 const scalar_type qop = track.qop();
0534 DETRAY_DEBUG_HOST_DEVICE("-> qop: %f", qop);
0535 DETRAY_DEBUG_HOST_DEVICE("-> dqopds: 0");
0536
0537 return detray::make_pair(scalar_type(0.f), qop);
0538 } else if (cfg.use_mean_loss && i != 0u) {
0539
0540
0541
0542
0543
0544
0545 const scalar_type qop = track.qop() + h * dqopds_prev;
0546 DETRAY_DEBUG_HOST_DEVICE("-> qop: %f", qop);
0547
0548 return detray::make_pair(stepping.dqopds(qop, vol_mat_ptr), qop);
0549 } else {
0550 const scalar_type qop = track.qop();
0551 DETRAY_DEBUG_HOST_DEVICE("-> qop: %f", qop);
0552
0553 return detray::make_pair(stepping.dqopds(qop, vol_mat_ptr), qop);
0554 }
0555 }
0556
0557
0558 DETRAY_HOST_DEVICE
0559 detray::pair<vector3_type, vector3_type> evaluate_dtds(
0560 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
0561 inspector_t, flags_v>::state& stepping,
0562 const vector3_type& b_field, const std::size_t i, const scalar_type h,
0563 const vector3_type& dtds_prev, const scalar_type qop) const {
0564 auto& track = stepping();
0565 const auto dir = track.dir();
0566
0567 assert(std::isfinite(qop));
0568
0569
0570 vector3_type t{(i == 0u || h == 0.f)
0571 ? dir
0572 : static_cast<vector3_type>(dir + h * dtds_prev)};
0573
0574
0575 DETRAY_DEBUG_HOST(
0576 "-> evaluate dtds: " << vector3_type{qop * vector::cross(t, b_field)});
0577
0578 return detray::make_pair(vector3_type{qop * vector::cross(t, b_field)}, t);
0579 }
0580
0581
0582 DETRAY_HOST_DEVICE
0583 void advance_track(
0584 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
0585 inspector_t, flags_v>::state& stepping,
0586 const intermediate_state& sd,
0587 const material<scalar_type>* vol_mat_ptr) const {
0588 const scalar_type h{stepping.step_size()};
0589 const scalar_type h_6{h * static_cast<scalar_type>(1. / 6.)};
0590 auto& track = stepping();
0591 auto pos = track.pos();
0592 auto dir = track.dir();
0593
0594
0595
0596 pos = pos + h * (sd.t[0u] + h_6 * (sd.dtds[0] + sd.dtds[1] + sd.dtds[2]));
0597 track.set_pos(pos);
0598
0599
0600 dir =
0601 dir + h_6 * (sd.dtds[0] + 2.f * (sd.dtds[1] + sd.dtds[2]) + sd.dtds[3]);
0602 dir = vector::normalize(dir);
0603 track.set_dir(dir);
0604
0605 auto qop = track.qop();
0606 if (vol_mat_ptr != nullptr) {
0607
0608
0609 qop = qop + h_6 * (sd.dqopds[0u] + 2.f * (sd.dqopds[1u] + sd.dqopds[2u]) +
0610 sd.dqopds[3u]);
0611 }
0612 track.set_qop(qop);
0613
0614
0615 stepping.update_path_lengths(h);
0616 }
0617
0618
0619 DETRAY_HOST_DEVICE
0620 void advance_jacobian(
0621 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
0622 inspector_t, flags_v>::state& stepping,
0623 const stepping::config& cfg, const intermediate_state& sd,
0624 const material<scalar_type>* vol_mat_ptr) const {
0625 DETRAY_VERBOSE_HOST_DEVICE("-> Advance Jacobian...");
0626
0627
0628
0629
0630
0631
0632
0633
0634
0635
0636
0637
0638
0639
0640
0641
0642
0643
0644
0645
0646
0647
0648 const scalar_type h{stepping.step_size()};
0649 const scalar_type h2{h * h};
0650 const scalar_type half_h{h * 0.5f};
0651 const scalar_type h_6{h * (1.f / 6.f)};
0652
0653
0654
0655
0656
0657
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667
0668
0669
0670
0671
0672
0673
0674
0675
0676
0677
0678
0679
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695
0696
0697
0698
0699
0700
0701
0702
0703
0704
0705
0706
0707
0708
0709
0710
0711
0712
0713
0714
0715
0716
0717
0718
0719
0720
0721
0722
0723
0724
0725
0726
0727
0728
0729
0730
0731
0732
0733
0734
0735
0736
0737
0738
0739
0740
0741
0742
0743
0744
0745
0746
0747
0748
0749 scalar_type dqopqop;
0750 vector3_type dFdqop;
0751 vector3_type dGdqop;
0752
0753 {
0754 darray<scalar_type, 4u> dqopn_dqop{1.f, 1.f, 1.f, 1.f};
0755
0756 if (!cfg.use_eloss_gradient) {
0757 dqopqop = 1.f;
0758 } else {
0759
0760 const scalar_type d2qop1dsdqop1 =
0761 stepping.d2qopdsdqop(sd.qop[0u], vol_mat_ptr);
0762
0763 dqopn_dqop[0u] = 1.f;
0764 dqopn_dqop[1u] = 1.f + half_h * d2qop1dsdqop1;
0765
0766 const scalar_type d2qop2dsdqop1 =
0767 stepping.d2qopdsdqop(sd.qop[1u], vol_mat_ptr) * dqopn_dqop[1u];
0768 dqopn_dqop[2u] = 1.f + half_h * d2qop2dsdqop1;
0769
0770 const scalar_type d2qop3dsdqop1 =
0771 stepping.d2qopdsdqop(sd.qop[2u], vol_mat_ptr) * dqopn_dqop[2u];
0772 dqopn_dqop[3u] = 1.f + h * d2qop3dsdqop1;
0773
0774 const scalar_type d2qop4dsdqop1 =
0775 stepping.d2qopdsdqop(sd.qop[3u], vol_mat_ptr) * dqopn_dqop[3u];
0776
0777
0778
0779
0780
0781 dqopqop =
0782 1.f + h_6 * (d2qop1dsdqop1 + 2.f * (d2qop2dsdqop1 + d2qop3dsdqop1) +
0783 d2qop4dsdqop1);
0784 }
0785
0786
0787
0788
0789
0790 {
0791 darray<vector3_type, 4u> dkndqop;
0792
0793
0794 dkndqop[0u] = dqopn_dqop[0u] * vector::cross(sd.t[0u], sd.b_first);
0795
0796
0797 dkndqop[1u] =
0798 dqopn_dqop[1u] * vector::cross(sd.t[1u], sd.b_middle) +
0799 sd.qop[1u] * half_h * vector::cross(dkndqop[0u], sd.b_middle);
0800
0801
0802 dkndqop[2u] =
0803 dqopn_dqop[2u] * vector::cross(sd.t[2u], sd.b_middle) +
0804 sd.qop[2u] * half_h * vector::cross(dkndqop[1u], sd.b_middle);
0805
0806
0807 dkndqop[3u] = dqopn_dqop[3u] * vector::cross(sd.t[3u], sd.b_last) +
0808 sd.qop[3u] * h * vector::cross(dkndqop[2u], sd.b_last);
0809
0810
0811 dFdqop = h * h_6 * (dkndqop[0u] + dkndqop[1u] + dkndqop[2u]);
0812 dGdqop = h_6 * (dkndqop[0u] + 2.f * (dkndqop[1u] + dkndqop[2u]) +
0813 dkndqop[3u]);
0814 }
0815 }
0816
0817
0818
0819
0820
0821 auto dFdt = matrix::identity<matrix_type<3, 3>>();
0822 auto dGdt = matrix::identity<matrix_type<3, 3>>();
0823
0824 {
0825 const auto I33 = matrix::identity<matrix_type<3, 3>>();
0826 darray<matrix_type<3u, 3u>, 4u> dkndt{I33, I33, I33, I33};
0827
0828
0829 dkndt[0u] = sd.qop[0u] * matrix::column_wise_cross(dkndt[0u], sd.b_first);
0830
0831
0832 dkndt[1u] = dkndt[1u] + half_h * dkndt[0u];
0833 dkndt[1u] =
0834 sd.qop[1u] * matrix::column_wise_cross(dkndt[1u], sd.b_middle);
0835
0836
0837 dkndt[2u] = dkndt[2u] + half_h * dkndt[1u];
0838 dkndt[2u] =
0839 sd.qop[2u] * matrix::column_wise_cross(dkndt[2u], sd.b_middle);
0840
0841
0842 dkndt[3u] = dkndt[3u] + h * dkndt[2u];
0843 dkndt[3u] = sd.qop[3u] * matrix::column_wise_cross(dkndt[3u], sd.b_last);
0844
0845 dFdt = dFdt + h_6 * (dkndt[0u] + dkndt[1u] + dkndt[2u]);
0846 dFdt = h * dFdt;
0847 dGdt =
0848 dGdt + h_6 * (dkndt[0u] + 2.f * (dkndt[1u] + dkndt[2u]) + dkndt[3u]);
0849 }
0850
0851
0852 auto dFdr = matrix::identity<matrix_type<3, 3>>();
0853 auto dGdr = matrix::zero<matrix_type<3, 3>>();
0854
0855 if constexpr ((flags_v & static_cast<std::uint32_t>(
0856 rk_stepper_flags::e_allow_field_gradient)) !=
0857 0u) {
0858 if (cfg.use_field_gradient) {
0859 darray<matrix_type<3u, 3u>, 4u> dkndr;
0860 auto& track = stepping();
0861
0862
0863
0864 vector3_type r_ini = track.pos();
0865 vector3_type r_mid =
0866 r_ini + half_h * sd.t[0u] + h2 * 0.125f * sd.dtds[0u];
0867 vector3_type r_fin = r_ini + h * sd.t[0u] + h2 * 0.5f * sd.dtds[2u];
0868
0869 matrix_type<3, 3> dBdr_ini = evaluate_field_gradient(stepping, r_ini);
0870 matrix_type<3, 3> dBdr_mid = evaluate_field_gradient(stepping, r_mid);
0871 matrix_type<3, 3> dBdr_fin = evaluate_field_gradient(stepping, r_fin);
0872
0873
0874
0875
0876 const auto I33 = matrix::identity<matrix_type<3, 3>>();
0877
0878
0879 dkndr[0u] = -sd.qop[0u] * matrix::column_wise_cross(dBdr_ini, sd.t[0u]);
0880
0881 const auto dkndr0_tmp = (I33 + h2 * 0.125f * dkndr[0u]);
0882
0883
0884 dkndr[1u] = sd.qop[1u] *
0885 matrix::column_wise_cross(half_h * dkndr[0u], sd.b_middle);
0886 dkndr[1u] =
0887 dkndr[1u] - sd.qop[1u] * matrix::column_wise_cross(
0888 dBdr_mid * dkndr0_tmp, sd.t[1u]);
0889
0890
0891 dkndr[2u] = sd.qop[2u] *
0892 matrix::column_wise_cross(half_h * dkndr[1u], sd.b_middle);
0893 dkndr[2u] =
0894 dkndr[2u] - sd.qop[2u] * matrix::column_wise_cross(
0895 dBdr_mid * dkndr0_tmp, sd.t[2u]);
0896
0897
0898 dkndr[3u] =
0899 sd.qop[3u] * matrix::column_wise_cross(h * dkndr[2u], sd.b_last);
0900 dkndr[3u] = dkndr[3u] -
0901 sd.qop[3u] *
0902 matrix::column_wise_cross(
0903 dBdr_fin * (I33 + h2 * 0.5f * dkndr[2u]), sd.t[3u]);
0904
0905
0906 dFdr = dFdr + h * h_6 * (dkndr[0u] + dkndr[1u] + dkndr[2u]);
0907 dGdr = h_6 * (dkndr[0u] + 2.f * (dkndr[1u] + dkndr[2u]) + dkndr[3u]);
0908 }
0909 } else {
0910 assert(!cfg.use_field_gradient);
0911 }
0912
0913 const auto old_jacobian = stepping.internal_transport_jacobian();
0914
0915 if constexpr ((flags_v & static_cast<std::uint32_t>(
0916 rk_stepper_flags::e_allow_field_gradient)) !=
0917 0u) {
0918 detail::update_transport_jacobian_with_gradient_impl(
0919 old_jacobian, dFdt, dGdt, dFdr, dGdr, dFdqop, dGdqop, dqopqop,
0920 stepping.internal_transport_jacobian());
0921 } else {
0922 assert(!cfg.use_field_gradient);
0923
0924 detail::update_transport_jacobian_without_gradient_impl(
0925 old_jacobian, dFdt, dGdt, dFdqop, dGdqop, dqopqop,
0926 stepping.internal_transport_jacobian());
0927 }
0928 }
0929
0930 DETRAY_HOST_DEVICE
0931 matrix_type<3, 3> evaluate_field_gradient(
0932 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
0933 inspector_t, flags_v>::state& stepping,
0934 const point3_type& pos) const {
0935 auto dBdr = matrix::zero<matrix_type<3, 3>>();
0936
0937 constexpr auto delta{1e-1f * unit<scalar_type>::mm};
0938
0939 for (unsigned int i = 0; i < 3; i++) {
0940 point3_type dpos1 = pos;
0941 dpos1[i] += delta;
0942 const auto bvec1_tmp = stepping.field().at(dpos1[0], dpos1[1], dpos1[2]);
0943 vector3_type bvec1;
0944 bvec1[0u] = bvec1_tmp[0u];
0945 bvec1[1u] = bvec1_tmp[1u];
0946 bvec1[2u] = bvec1_tmp[2u];
0947
0948 point3_type dpos2 = pos;
0949 dpos2[i] -= delta;
0950 const auto bvec2_tmp = stepping.field().at(dpos2[0], dpos2[1], dpos2[2]);
0951 vector3_type bvec2;
0952 bvec2[0u] = bvec2_tmp[0u];
0953 bvec2[1u] = bvec2_tmp[1u];
0954 bvec2[2u] = bvec2_tmp[2u];
0955
0956 assert(delta != 0.f);
0957 const vector3_type gradient = (bvec1 - bvec2) * (1.f / (2.f * delta));
0958
0959 getter::element(dBdr, 0u, i) = gradient[0u];
0960 getter::element(dBdr, 1u, i) = gradient[1u];
0961 getter::element(dBdr, 2u, i) = gradient[2u];
0962 }
0963
0964 return dBdr;
0965 }
0966 };
0967
0968 }