ACTS
Experiment-independent tracking
Loading...
Searching...
No Matches
rk_stepper.hpp
1// This file is part of the ACTS project.
2//
3// Copyright (C) 2016 CERN for the benefit of the ACTS project
4//
5// This Source Code Form is subject to the terms of the Mozilla Public
6// License, v. 2.0. If a copy of the MPL was not distributed with this
7// file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9#pragma once
10
11// Project include(s).
12#include "detray/core/concepts.hpp"
13#include "detray/definitions/algebra.hpp"
14#include "detray/definitions/detail/qualifiers.hpp"
15#include "detray/definitions/units.hpp"
16#include "detray/material/interaction.hpp"
17#include "detray/material/material.hpp"
18#include "detray/navigation/policies.hpp"
19#include "detray/propagator/base_stepper.hpp"
20#include "detray/propagator/detail/codegen/update_rk_transport_jacobian.hpp"
21#include "detray/propagator/transport_jacobian.hpp"
22#include "detray/tracks/tracks.hpp"
23
24namespace detray {
32
41template <typename magnetic_field_t, concepts::algebra algebra_t,
42 typename constraint_t = unconstrained_step<dscalar<algebra_t>>,
43 typename policy_t = stepper_rk_policy<dscalar<algebra_t>>,
44 typename inspector_t = stepping::void_inspector,
45 std::uint32_t flags_v =
46 (static_cast<std::uint32_t>(
48 static_cast<std::uint32_t>(
50class rk_stepper final
51 : public base_stepper<algebra_t, constraint_t, policy_t, inspector_t> {
52 using base_type =
54
55 public:
57 static constexpr bool uses_gradient = static_cast<bool>(
58 flags_v &
59 static_cast<std::uint32_t>(rk_stepper_flags::e_allow_field_gradient));
60
62 using algebra_type = algebra_t;
78 using magnetic_field_type = magnetic_field_t;
80 template <std::size_t ROWS, std::size_t COLS>
83 using transport_jacobian_type = std::conditional_t<
85 detail::transport_jacobian_matrix_with_gradient<algebra_type>,
86 detail::transport_jacobian_matrix_without_gradient<algebra_type>>;
87
88 rk_stepper() = default;
89
107
109 struct state : public base_type::template state<transport_jacobian_type> {
111 static constexpr const stepping::id id = stepping::id::e_rk;
112
115 typename base_type::template state<transport_jacobian_type>;
116
120 DETRAY_HOST_DEVICE
122 const magnetic_field_t& mag_field)
123 : base_state(t), m_magnetic_field(mag_field) {}
124
130 template <concepts::detector detector_t>
131 DETRAY_HOST_DEVICE state(const bound_track_parameters_type& bound_params,
132 const magnetic_field_t& mag_field,
133 const detector_t& det,
134 const typename detector_t::geometry_context& ctx)
135 : base_state(bound_params, det, ctx), m_magnetic_field(mag_field) {}
136
138 DETRAY_HOST_DEVICE
139 const magnetic_field_type& field() const { return m_magnetic_field; }
140
143 DETRAY_HOST_DEVICE
145 assert(math::fabs(step) >= 1e-4f * unit<scalar_type>::mm);
146 m_next_step_size = step;
147 }
148
150 DETRAY_HOST_DEVICE
151 inline scalar_type next_step_size() const { return m_next_step_size; }
152
155 DETRAY_HOST_DEVICE
157 // In case there was no step before
158 if (this->path_length() == 0.f) [[unlikely]] {
159 const point3_type pos = (*this)().pos();
160
161 const auto bvec_tmp = this->field().at(pos[0], pos[1], pos[2]);
162 vector3_type bvec;
163 bvec[0u] = bvec_tmp[0u];
164 bvec[1u] = bvec_tmp[1u];
165 bvec[2u] = bvec_tmp[2u];
166
167 DETRAY_DEBUG_HOST(
168 "--> dtds: " << (*this)().qop() *
169 vector::cross((*this)().dir(), bvec));
170
171 return (*this)().qop() * vector::cross((*this)().dir(), bvec);
172 }
173
174 return m_dtds_3;
175 }
176
179 DETRAY_HOST_DEVICE
180 void dtds(const vector3_type& v) { m_dtds_3 = v; }
181
185 DETRAY_HOST_DEVICE
186 scalar_type dqopds(const material<scalar_type>* vol_mat_ptr) const {
187 // In case there was no step before
188 if (this->path_length() == 0.f) [[unlikely]] {
189 return this->dqopds((*this)().qop(), vol_mat_ptr);
190 }
191
192 return m_dqopds_3;
193 }
194
199 DETRAY_HOST_DEVICE
201 const material<scalar_type>* vol_mat_ptr) const {
202 // d(qop)ds is zero for empty space
203 if (vol_mat_ptr == nullptr) {
204 return 0.f;
205 }
206
207 assert(qop != 0.f);
208 assert(std::isfinite(qop));
209 const scalar_type q = this->particle_hypothesis().charge();
210 const scalar_type p = q / qop;
211 const scalar_type mass = this->particle_hypothesis().mass();
212 const scalar_type E = math::sqrt(p * p + mass * mass);
213
214 // Compute stopping power
215 const scalar_type stopping_power =
217 *vol_mat_ptr, this->particle_hypothesis(), {mass, qop, q});
218
219 // Assert that a momentum is a positive value
220 assert(p >= 0.f);
221 assert(q != 0.f);
222
223 DETRAY_DEBUG_HOST_DEVICE("--> dqopds: %f",
224 qop * qop * qop * E * stopping_power / (q * q));
225
226 // d(qop)ds, which is equal to (qop) * E * (-dE/ds) / p^2
227 // or equal to (qop)^3 * E * (-dE/ds) / q^2
228 return qop * qop * qop * E * stopping_power / (q * q);
229 }
230
233 DETRAY_HOST_DEVICE void dqopds(const scalar_type& s) { m_dqopds_3 = s; }
234
239 DETRAY_HOST_DEVICE
241 const material<scalar_type>* vol_mat_ptr) const {
242 if (vol_mat_ptr == nullptr) {
243 return 0.f;
244 }
245
246 const scalar_type q = this->particle_hypothesis().charge();
247 const scalar_type p = q / qop;
248 const scalar_type p2 = p * p;
249
250 const auto& mass = this->particle_hypothesis().mass();
251 const scalar_type E2 = p2 + mass * mass;
252
253 // Interaction object
255
256 // g = dE/ds = -1 * (-dE/ds) = -1 * stopping power
257 const detail::relativistic_quantities<scalar_type> rq(mass, qop, q);
258 const scalar_type g =
259 -1.f * I.compute_stopping_power(*vol_mat_ptr,
260 this->particle_hypothesis(), rq);
261
262 // dg/d(qop) = -1 * derivation of stopping power
263 const scalar_type dgdqop =
264 -1.f * I.derive_stopping_power(*vol_mat_ptr,
265 this->particle_hypothesis(), rq);
266
267 // d(qop)/ds = - qop^3 * E * g / q^2
268 const scalar_type dqopds = this->dqopds(qop, vol_mat_ptr);
269
270 // Check Eq 3.12 of
271 // (https://iopscience.iop.org/article/10.1088/1748-0221/4/04/P04016/meta)
272 assert(E2 != 0.f);
273 assert(g != 0.f);
274 return dqopds * (1.f / qop * (3.f - p2 / E2) + 1.f / g * dgdqop);
275 }
276
282 template <typename... Args>
283 DETRAY_HOST_DEVICE void run_inspector(
284 [[maybe_unused]] const stepping::config& cfg,
285 [[maybe_unused]] const char* message,
286 [[maybe_unused]] const scalar_type dist,
287 [[maybe_unused]] Args&&... args) {
288 if constexpr (!std::is_same_v<inspector_t, stepping::void_inspector>) {
289 this->inspector()(*this, cfg, message, dist,
290 std::forward<Args>(args)...);
291 }
292
293 if constexpr (sizeof...(Args) > 0u) {
294 DETRAY_DEBUG_HOST("" << message << "\n"
295 << detray::stepping::print_state(
296 *this, std::forward<Args>(args)...));
297 }
298 }
299
300 private:
301 vector3_type m_dtds_3;
302 scalar_type m_dqopds_3;
303
305 scalar_type m_next_step_size{0.f};
306
308 const magnetic_field_type& m_magnetic_field;
309 };
310
320 DETRAY_HOST_DEVICE bool step(
321 const scalar_type dist_to_next, state& stepping,
322 const stepping::config& cfg, bool do_reset,
323 const material<scalar_type>* vol_mat_ptr = nullptr) const {
324 DETRAY_DEBUG_HOST("Before: " << stepping());
325
326 // In case of an overlap do nothing
327 if (math::fabs(dist_to_next) < 1.f * unit<float>::um) [[unlikely]] {
328 DETRAY_VERBOSE_HOST_DEVICE("Zero stepsize...");
329
330 // Don't allow a too small step size on next step
331 if (math::fabs(stepping.next_step_size()) < cfg.min_stepsize) {
332 stepping.set_next_step_size(math::copysign(
333 static_cast<scalar_type>(cfg.min_stepsize), dist_to_next));
334 }
335 DETRAY_DEBUG_HOST_DEVICE("Setting next stepsize: %f mm",
336 stepping.next_step_size());
337
338 stepping.run_inspector(cfg, "Step skipped (Overlap): ", dist_to_next);
339
340 DETRAY_DEBUG_HOST("After: " << stepping());
341 return true;
342 }
343
344 if (!(do_reset ||
345 math::fabs(stepping.next_step_size()) >= cfg.min_stepsize)) {
346 DETRAY_INFO_HOST("Next stepsize: " << stepping.next_step_size() << ", "
347 << cfg.min_stepsize);
348 }
349
350 // Check navigator and actor results
351 assert(math::fabs(dist_to_next) != 0.f);
352 assert(do_reset ||
353 math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
354 assert(!stepping().is_invalid());
355
356 // Get stepper and navigator states
357 auto& magnetic_field = stepping.field();
358
359 if (do_reset) {
360 stepping.set_step_size(dist_to_next);
361 } else if (stepping.next_step_size() > 0) {
362 assert(math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
363 stepping.set_step_size(
364 math::min(stepping.next_step_size(), dist_to_next));
365 } else {
366 assert(math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
367 stepping.set_step_size(
368 math::max(stepping.next_step_size(), dist_to_next));
369 }
370
371 DETRAY_VERBOSE_HOST_DEVICE("Distance to nex: %f mm", dist_to_next);
372 DETRAY_VERBOSE_HOST_DEVICE("Initial stepsize: %f mm", stepping.step_size());
373
374 // Don't allow too small stepsizes, unless the navigation needs it
375 const scalar_type min_stepsize{
376 math::min(math::fabs(stepping.step_size()),
377 static_cast<scalar_type>(cfg.min_stepsize))};
378 const point3_type pos = stepping().pos();
379
381
382 // First Runge-Kutta point
383 auto bvec = magnetic_field.at(pos[0], pos[1], pos[2]);
384 assert(math::isfinite(bvec[0]));
385 assert(math::isfinite(bvec[1]));
386 assert(math::isfinite(bvec[2]));
387 sd.b_first[0] = bvec[0];
388 sd.b_first[1] = bvec[1];
389 sd.b_first[2] = bvec[2];
390
391 DETRAY_DEBUG_HOST_DEVICE("First stage:");
392 DETRAY_DEBUG_HOST_DEVICE("-> B-field: [%f, %f, %f]", bvec[0], bvec[1],
393 bvec[2]);
394
395 // qop should be recalculated at every point
396 // Reference: Eq (84) of https://doi.org/10.1016/0029-554X(81)90063-X
397 detray::tie(sd.dqopds[0u], sd.qop[0u]) =
398 evaluate_dqopds(stepping, 0u, 0.f, 0.f, vol_mat_ptr, cfg);
399 detray::tie(sd.dtds[0u], sd.t[0u]) = evaluate_dtds(
400 stepping, sd.b_first, 0u, 0.f, vector3_type{0.f, 0.f, 0.f}, sd.qop[0u]);
401
403 const auto estimate_error = [&](const scalar_type& h) {
404 assert(h != 0);
405 // State the square and half of the step size
406 const scalar_type h2{h * h};
407 const scalar_type half_h{h * 0.5f};
408
409 // Second Runge-Kutta point
410 // qop should be recalculated at every point
411 // Eq (84) of https://doi.org/10.1016/0029-554X(81)90063-X
412 const point3_type pos1 =
413 pos + half_h * sd.t[0u] + h2 * 0.125f * sd.dtds[0u];
414 bvec = magnetic_field.at(pos1[0], pos1[1], pos1[2]);
415 assert(math::isfinite(bvec[0]));
416 assert(math::isfinite(bvec[1]));
417 assert(math::isfinite(bvec[2]));
418 sd.b_middle[0] = bvec[0];
419 sd.b_middle[1] = bvec[1];
420 sd.b_middle[2] = bvec[2];
421 DETRAY_DEBUG_HOST_DEVICE("Second stage:");
422 DETRAY_DEBUG_HOST_DEVICE("-> B-field: [%f, %f, %f]", bvec[0], bvec[1],
423 bvec[2]);
424
425 detray::tie(sd.dqopds[1u], sd.qop[1u]) = evaluate_dqopds(
426 stepping, 1u, half_h, sd.dqopds[0u], vol_mat_ptr, cfg);
427 detray::tie(sd.dtds[1u], sd.t[1u]) = evaluate_dtds(
428 stepping, sd.b_middle, 1u, half_h, sd.dtds[0u], sd.qop[1u]);
429
430 // Third Runge-Kutta point
431 // qop should be recalculated at every point
432 // Reference: Eq (84) of
433 // https://doi.org/10.1016/0029-554X(81)90063-X
434 detray::tie(sd.dqopds[2u], sd.qop[2u]) = evaluate_dqopds(
435 stepping, 2u, half_h, sd.dqopds[1u], vol_mat_ptr, cfg);
436 detray::tie(sd.dtds[2u], sd.t[2u]) = evaluate_dtds(
437 stepping, sd.b_middle, 2u, half_h, sd.dtds[1u], sd.qop[2u]);
438
439 // Last Runge-Kutta point
440 // qop should be recalculated at every point
441 // Eq (84) of https://doi.org/10.1016/0029-554X(81)90063-X
442 const point3_type pos2 = pos + h * sd.t[0u] + h2 * 0.5f * sd.dtds[2u];
443 bvec = magnetic_field.at(pos2[0], pos2[1], pos2[2]);
444 assert(math::isfinite(bvec[0]));
445 assert(math::isfinite(bvec[1]));
446 assert(math::isfinite(bvec[2]));
447 sd.b_last[0] = bvec[0];
448 sd.b_last[1] = bvec[1];
449 sd.b_last[2] = bvec[2];
450
451 DETRAY_DEBUG_HOST_DEVICE("Third stage:");
452 DETRAY_DEBUG_HOST_DEVICE("-> B-field: [%f, %f, %f]", bvec[0], bvec[1],
453 bvec[2]);
454
455 detray::tie(sd.dqopds[3u], sd.qop[3u]) =
456 evaluate_dqopds(stepping, 3u, h, sd.dqopds[2u], vol_mat_ptr, cfg);
457 detray::tie(sd.dtds[3u], sd.t[3u]) =
458 evaluate_dtds(stepping, sd.b_last, 3u, h, sd.dtds[2u], sd.qop[3u]);
459
460 // Compute and check the local integration error estimate
461 // @Todo
462 constexpr auto one_sixth{static_cast<scalar_type>(1. / 6.)};
463 const vector3_type err_vec =
464 one_sixth * h2 *
465 (sd.dtds[0u] - sd.dtds[1u] - sd.dtds[2u] + sd.dtds[3u]);
466
467 DETRAY_DEBUG_HOST("-> Integration error vector: " << err_vec);
468
469 return vector::norm(err_vec);
470 };
471
474 const auto step_size_scaling =
475 [&cfg](const scalar_type& err) -> scalar_type {
476 assert(err != 0.f);
477 return static_cast<scalar_type>(
478 math::min(math::max(math::sqrt(math::sqrt(cfg.rk_error_tol / err)),
479 static_cast<scalar_type>(0.25)),
480 static_cast<scalar_type>(4.)));
481 };
482
483 scalar_type error{1e20f};
484
485 // If the estimated error is larger than the tolerance with an
486 // additional margin, reduce the step size and try again
487 const auto n_trials{cfg.max_rk_updates};
488 for (unsigned int i = 0u; i < n_trials; i++) {
489 stepping.count_trials();
490
491 error = math::max(estimate_error(stepping.step_size()),
492 static_cast<scalar_type>(1e-20));
493
494 DETRAY_DEBUG_HOST_DEVICE("-> Integration error: %f", error);
495 assert(math::isfinite(error));
496
497 // Error is small enough
498 // ---> break and advance track
499 if (error <= 4.f * cfg.rk_error_tol) {
500 break;
501 }
502 // Error estimate is too big
503 // ---> Make step size smaller and estimate error again
504 else {
505 stepping.set_step_size(stepping.step_size() * step_size_scaling(error));
506
507 // Run inspection while the stepsize is getting adjusted
508 stepping.run_inspector(cfg, "Adjust stepsize: ", dist_to_next, i,
509 step_size_scaling(error));
510 }
511 }
512
513 stepping.dtds(sd.dtds[3u]);
514 stepping.dqopds(sd.dqopds[3u]);
515
516 // Check constraints
517 if (const scalar_type max_step =
518 stepping.constraints().template size<>(stepping.direction());
519 math::fabs(stepping.step_size()) > math::fabs(max_step)) {
520 // Run inspection before step size is cut
521 stepping.run_inspector(cfg, "Before constraint: ", dist_to_next);
522
523 stepping.set_step_size(max_step);
524 }
525
526 // Adjust the min step size
527 if (math::fabs(stepping.step_size()) < min_stepsize) {
528 stepping.set_step_size(
529 math::copysign(min_stepsize, stepping.step_size()));
530 }
531
532 DETRAY_VERBOSE_HOST_DEVICE("Take step: %f mm", stepping.step_size());
533
534 // The step size estimation for the next step
535 stepping.set_next_step_size(stepping.step_size() *
536 step_size_scaling(error));
537
538 // Don't allow a too small step size
539 if (math::fabs(stepping.next_step_size()) < cfg.min_stepsize) {
540 stepping.set_next_step_size(
541 math::copysign(static_cast<scalar_type>(cfg.min_stepsize),
542 stepping.next_step_size()));
543 }
544
545 DETRAY_DEBUG_HOST_DEVICE("Estimated next stepsize: %f mm",
546 stepping.next_step_size());
547
548 assert(math::fabs(stepping.next_step_size()) >= cfg.min_stepsize);
549 assert(math::fabs(stepping.step_size()) >= cfg.min_stepsize);
550
551 // Advance track state
552 advance_track(stepping, sd, vol_mat_ptr);
553 assert(!stepping().is_invalid());
554
555 // Advance jacobian transport
556 if constexpr ((flags_v & static_cast<std::uint32_t>(
557 detray::rk_stepper_flags::
558 e_allow_covariance_transport)) != 0u) {
559 if (cfg.do_covariance_transport) {
560 advance_jacobian(stepping, cfg, sd, vol_mat_ptr);
561 }
562 } else {
563 assert(!cfg.do_covariance_transport);
564 }
565
566 // Run final inspection
567 stepping.run_inspector(cfg, "Step complete: ", dist_to_next);
568
569 DETRAY_DEBUG_HOST("After: " << stepping());
570 return true;
571 }
572
581 DETRAY_HOST_DEVICE
583 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
584 inspector_t, flags_v>::state& stepping,
585 const std::size_t i, const scalar_type h, const scalar_type dqopds_prev,
586 const material<scalar_type>* vol_mat_ptr,
587 const detray::stepping::config& cfg) const {
588 const auto& track = stepping();
589
590 if (vol_mat_ptr == nullptr) {
591 const scalar_type qop = track.qop();
592 DETRAY_DEBUG_HOST_DEVICE("-> qop: %f", qop);
593 DETRAY_DEBUG_HOST_DEVICE("-> dqopds: 0");
594
595 return detray::make_pair(scalar_type(0.f), qop);
596 } else if (cfg.use_mean_loss && i != 0u) {
597 // qop_n is calculated recursively like the direction of
598 // evaluate_dtds.
599 //
600 // https://doi.org/10.1016/0029-554X(81)90063-X says:
601 // "For y we have similar formulae as for x, for y' and
602 // \lambda similar formulae as for x'"
603 const scalar_type qop = track.qop() + h * dqopds_prev;
604 DETRAY_DEBUG_HOST_DEVICE("-> qop: %f", qop);
605
606 return detray::make_pair(stepping.dqopds(qop, vol_mat_ptr), qop);
607 } else {
608 const scalar_type qop = track.qop();
609 DETRAY_DEBUG_HOST_DEVICE("-> qop: %f", qop);
610
611 return detray::make_pair(stepping.dqopds(qop, vol_mat_ptr), qop);
612 }
613 }
614
623 DETRAY_HOST_DEVICE
625 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
626 inspector_t, flags_v>::state& stepping,
627 const vector3_type& b_field, const std::size_t i, const scalar_type h,
628 const vector3_type& dtds_prev, const scalar_type qop) const {
629 auto& track = stepping();
630 const auto dir = track.dir();
631
632 assert(std::isfinite(qop));
633
634 // Eq (84) of https://doi.org/10.1016/0029-554X(81)90063-X
635 vector3_type t{(i == 0u || h == 0.f)
636 ? dir
637 : static_cast<vector3_type>(dir + h * dtds_prev)};
638
639 // dtds = qop * (t X B) from Lorentz force
640 DETRAY_DEBUG_HOST(
641 "-> evaluate dtds: " << vector3_type{qop * vector::cross(t, b_field)});
642
643 return detray::make_pair(vector3_type{qop * vector::cross(t, b_field)}, t);
644 }
645
650 DETRAY_HOST_DEVICE
652 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
653 inspector_t, flags_v>::state& stepping,
654 const intermediate_state& sd,
655 const material<scalar_type>* vol_mat_ptr) const {
656 const scalar_type h{stepping.step_size()};
657 const scalar_type h_6{h * static_cast<scalar_type>(1. / 6.)};
658 auto& track = stepping();
659 auto pos = track.pos();
660 auto dir = track.dir();
661
662 // Update the track parameters according to the equations of motion
663 // Reference: Eq (82) of https://doi.org/10.1016/0029-554X(81)90063-X
664 pos = pos + h * (sd.t[0u] + h_6 * (sd.dtds[0] + sd.dtds[1] + sd.dtds[2]));
665 track.set_pos(pos);
666
667 // Reference: Eq (82) of https://doi.org/10.1016/0029-554X(81)90063-X
668 dir =
669 dir + h_6 * (sd.dtds[0] + 2.f * (sd.dtds[1] + sd.dtds[2]) + sd.dtds[3]);
670 dir = vector::normalize(dir);
671 track.set_dir(dir);
672
673 auto qop = track.qop();
674 if (vol_mat_ptr != nullptr) {
675 // Reference: Eq (82) of
676 // https://doi.org/10.1016/0029-554X(81)90063-X
677 qop = qop + h_6 * (sd.dqopds[0u] + 2.f * (sd.dqopds[1u] + sd.dqopds[2u]) +
678 sd.dqopds[3u]);
679 }
680 track.set_qop(qop);
681
682 // Update path lengths
683 stepping.update_path_lengths(h);
684 }
685
691 DETRAY_HOST_DEVICE
693 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
694 inspector_t, flags_v>::state& stepping,
695 const stepping::config& cfg, const intermediate_state& sd,
696 const material<scalar_type>* vol_mat_ptr) const {
697 DETRAY_VERBOSE_HOST_DEVICE("-> Advance Jacobian...");
698
719
720 const scalar_type h{stepping.step_size()};
721 const scalar_type h2{h * h};
722 const scalar_type half_h{h * 0.5f};
723 const scalar_type h_6{h * (1.f / 6.f)};
724
725 /*---------------------------------------------------------------------------
726 * dk_n/dt1
727 * = qop_n * (dt_n/dt1 X B_n)
728 * + qop_n * ( t_n X dB_n/dt1 ),
729 * where dB_n/dt1 == dB_n/dr_n * dr_n/dt1.
730 *
731 * The second term is non-zero only for inhomogeneous magnetic fields
732 *
733 * Note that [ t_n = t1 + h * d(t_{n-1})/ds) ] as indicated by
734 * Eq (84) of https://doi.org/10.1016/0029-554X(81)90063-X
735
736 * [ Table for dt_n/dt1 ]
737 * dt1/dt1 = I
738 * dt2/dt1 = d( t1 + h/2 * dt1/ds ) / dt1 = I + h/2 * dk1/dt1
739 * dt3/dt1 = d( t1 + h/2 * dt2/ds ) / dt1 = I + h/2 * dk2/dt1
740 * dt4/dt1 = d( t1 + h * dt3/ds ) / dt1 = I + h * dk3/dt1
741 *
742 * [ Table for dr_n/dt1 ]
743 * dr1/dt1 = 0
744 * dr2/dt1 = d(r1 + h/2 * t1 + h^2/8 dt1/ds)/dt1 = h/2 * I + h^2/8
745 dk1/dt1
746 * dr3/dt1 = d(r1 + h/2 * t1 + h^2/8 dt1/ds)/dt1 = h/2 * I + h^2/8
747 dk1/dt1
748 * dr4/dt1 = d(r1 + h * t1 + h^2/2 dt3/ds)/dt1 = h * I + h^2/2 dk3/dt1
749 *
750 * Note that
751 * d/dr [ F(T) X B ] = dF(T)/dr (X) B, where (X) means the column wise
752 * cross product
753 ---------------------------------------------------------------------------*/
754
755 /*---------------------------------------------------------------------------
756 * dk_n/dqop_1
757 * = dqop_n/dqop1 * ( t_n X B_n )
758 * + qop_n * ( dt_n/dqop1 X B_n )
759 * + qop_n * ( t_n X dB_n/dqop1 ),
760 * where dB_n/dqop1 = dB_n/dr_n * dr_n/dqop1
761 *
762 * Note that [ qop_n = qop1 + h * dqop_{n-1}/ds) ] as indicated by
763 * Eq (84) of https://doi.org/10.1016/0029-554X(81)90063-X
764 *
765 * [ Table for dqop_n/dqop1 ]
766 * dqop1/dqop1 = 1
767 * dqop2/dqop1 = 1 + h/2 * d(dqop1/ds)/dqop1
768 * dqop3/dqop1 = 1 + h/2 * d(dqop2/ds)/dqop1
769 * dqop4/dqop1 = 1 + h * d(dqop3/ds)/dqop1
770 *
771 * [ Table for dt_n/dqop1 ]
772 * dt1/dqop1 = 0
773 * dt2/dqop1 = d(t1 + h/2 dt1/ds)/dqop1 = h/2 * dk1/dqop1
774 * dt3/dqop1 = d(t1 + h/2 dt2/ds)/dqop1 = h/2 * dk2/dqop1
775 * dt4/dqop1 = d(t1 + h dt3/ds)/dqop1 = h * dk3/dqop1
776 *
777 * [ Table for dr_n/dqop1 ]
778 * dr1/dqop1 = 0
779 * dr2/dqop1 = d(r1 + h/2 * t1 + h^2/8 dt1/ds)/dqop1 = h^2/8 *
780 dk1/dqop1
781 * dr3/dqop1 = d(r1 + h/2 * t1 + h^2/8 dt1/ds)/dqop1 = h^2/8 *
782 dk1/dqop1
783 * dr4/dqop1 = d(r1 + h * t1 + h^2/2 dt3/ds)/dqop1 = h^2/2 dk3/dqop1
784 ---------------------------------------------------------------------------*/
785
786 /*---------------------------------------------------------------------------
787 * dk_n/dr1
788 * = qop_n * ( dt_n/dr1 X B_n )
789 * + qop_n * ( t_n X dB_n/dr1 ),
790 * where dB_n/dr1 = dB_n/dr_n * dr_n/dr1
791 *
792 * [ Table for dt_n/dr1 ]
793 * dt1/dr1 = 0
794 * dt2/dr1 = d(t1 + h/2 * dt1/ds)/dr1 = h/2 * dk1/dr1
795 * dt2/dr1 = d(t1 + h/2 * dt2/ds)/dr1 = h/2 * dk2/dr1
796 * dt3/dr1 = d(t1 + h * dt3/ds)/dr1 = h * dk3/dr1
797 *
798 * [ Table for dr_n/dr1 ]
799 * dr1/dr1 = I
800 * dr2/dr1 = (r1 + h/2 * t1 + h^2/8 dt1/ds ) / dr1 = I + h^2/8 dk1/dr1
801 * dr3/dr1 = (r1 + h/2 * t1 + h^2/8 dt1/ds ) / dr1 = I + h^2/8 dk1/dr1
802 * dr4/dr1 = (r1 + h * t1 + h^2/2 dt3/ds ) / dr1 = I + h^2/2 dk3/dr1
803 ---------------------------------------------------------------------------*/
804
805 /*---------------------------------------------------------------------------
806 * d(dqop_n/ds)/dqop1
807 *
808 * Useful equation:
809 * dqop/ds = qop^3 * E * (-dE/ds) / q^2 = - qop^3 * E g / q^2
810
811 * [ Table for d(dqop_n/ds)/dqop1 ]
812 * d(dqop1/ds)/dqop1 = dqop1/ds * (1/qop * (3 - p^2/E^2) + 1/g1 *
813 dg1dqop1
814 * d(dqop2/ds)/dqop1 = d(dqop2/ds)/dqop2 * (1 + h/2 *
815 d(dqop1/ds)/dqop1)
816 * d(dqop3/ds)/dqop1 = d(dqop3/ds)/dqop3 * (1 + h/2 *
817 d(dqop2/ds)/dqop1)
818 * d(dqop4/ds)/dqop1 = d(dqop4/ds)/dqop4 * (1 + h * d(dqop3/ds)/dqop1)
819 ---------------------------------------------------------------------------*/
820
821 scalar_type dqopqop;
822 vector3_type dFdqop;
823 vector3_type dGdqop;
824
825 {
826 darray<scalar_type, 4u> dqopn_dqop{1.f, 1.f, 1.f, 1.f};
827
828 if (!cfg.use_eloss_gradient) {
829 dqopqop = 1.f;
830 } else {
831 // Pre-calculate dqop_n/dqop1
832 const scalar_type d2qop1dsdqop1 =
833 stepping.d2qopdsdqop(sd.qop[0u], vol_mat_ptr);
834
835 dqopn_dqop[0u] = 1.f;
836 dqopn_dqop[1u] = 1.f + half_h * d2qop1dsdqop1;
837
838 const scalar_type d2qop2dsdqop1 =
839 stepping.d2qopdsdqop(sd.qop[1u], vol_mat_ptr) * dqopn_dqop[1u];
840 dqopn_dqop[2u] = 1.f + half_h * d2qop2dsdqop1;
841
842 const scalar_type d2qop3dsdqop1 =
843 stepping.d2qopdsdqop(sd.qop[2u], vol_mat_ptr) * dqopn_dqop[2u];
844 dqopn_dqop[3u] = 1.f + h * d2qop3dsdqop1;
845
846 const scalar_type d2qop4dsdqop1 =
847 stepping.d2qopdsdqop(sd.qop[3u], vol_mat_ptr) * dqopn_dqop[3u];
848
849 /*-----------------------------------------------------------------
850 * Calculate the first terms of d(dqop_n/ds)/dqop1
851 -------------------------------------------------------------------*/
852
853 dqopqop =
854 1.f + h_6 * (d2qop1dsdqop1 + 2.f * (d2qop2dsdqop1 + d2qop3dsdqop1) +
855 d2qop4dsdqop1);
856 }
857
858 /*-----------------------------------------------------------------
859 * Calculate the first and second terms of dk_n/dqop1
860 -------------------------------------------------------------------*/
861
862 {
864
865 // dk1/dqop1
866 dkndqop[0u] = dqopn_dqop[0u] * vector::cross(sd.t[0u], sd.b_first);
867
868 // dk2/dqop1
869 dkndqop[1u] =
870 dqopn_dqop[1u] * vector::cross(sd.t[1u], sd.b_middle) +
871 sd.qop[1u] * half_h * vector::cross(dkndqop[0u], sd.b_middle);
872
873 // dk3/dqop1
874 dkndqop[2u] =
875 dqopn_dqop[2u] * vector::cross(sd.t[2u], sd.b_middle) +
876 sd.qop[2u] * half_h * vector::cross(dkndqop[1u], sd.b_middle);
877
878 // dk4/dqop1
879 dkndqop[3u] = dqopn_dqop[3u] * vector::cross(sd.t[3u], sd.b_last) +
880 sd.qop[3u] * h * vector::cross(dkndqop[2u], sd.b_last);
881
882 // Set dF/dqop1 and dG/dqop1
883 dFdqop = h * h_6 * (dkndqop[0u] + dkndqop[1u] + dkndqop[2u]);
884 dGdqop = h_6 * (dkndqop[0u] + 2.f * (dkndqop[1u] + dkndqop[2u]) +
885 dkndqop[3u]);
886 }
887 }
888
889 /*-----------------------------------------------------------------
890 * Calculate the first terms of dk_n/dt1
891 -------------------------------------------------------------------*/
892 // Set dF/dt1 and dG/dt1
893 auto dFdt = matrix::identity<matrix_type<3, 3>>();
894 auto dGdt = matrix::identity<matrix_type<3, 3>>();
895
896 {
897 const auto I33 = matrix::identity<matrix_type<3, 3>>();
898 darray<matrix_type<3u, 3u>, 4u> dkndt{I33, I33, I33, I33};
899
900 // dk1/dt1
901 dkndt[0u] = sd.qop[0u] * matrix::column_wise_cross(dkndt[0u], sd.b_first);
902
903 // dk2/dt1
904 dkndt[1u] = dkndt[1u] + half_h * dkndt[0u];
905 dkndt[1u] =
906 sd.qop[1u] * matrix::column_wise_cross(dkndt[1u], sd.b_middle);
907
908 // dk3/dt1
909 dkndt[2u] = dkndt[2u] + half_h * dkndt[1u];
910 dkndt[2u] =
911 sd.qop[2u] * matrix::column_wise_cross(dkndt[2u], sd.b_middle);
912
913 // dk4/dt1
914 dkndt[3u] = dkndt[3u] + h * dkndt[2u];
915 dkndt[3u] = sd.qop[3u] * matrix::column_wise_cross(dkndt[3u], sd.b_last);
916
917 dFdt = dFdt + h_6 * (dkndt[0u] + dkndt[1u] + dkndt[2u]);
918 dFdt = h * dFdt;
919 dGdt =
920 dGdt + h_6 * (dkndt[0u] + 2.f * (dkndt[1u] + dkndt[2u]) + dkndt[3u]);
921 }
922
923 // Calculate dkndr in case of considering B field gradient
924 auto dFdr = matrix::identity<matrix_type<3, 3>>();
925 auto dGdr = matrix::zero<matrix_type<3, 3>>();
926
927 if constexpr ((flags_v & static_cast<std::uint32_t>(
929 0u) {
930 if (cfg.use_field_gradient) {
932 auto& track = stepping();
933
934 // Positions and field gradients at initial, middle and final
935 // points of the fourth order RKN
936 vector3_type r_ini = track.pos();
937 vector3_type r_mid =
938 r_ini + half_h * sd.t[0u] + h2 * 0.125f * sd.dtds[0u];
939 vector3_type r_fin = r_ini + h * sd.t[0u] + h2 * 0.5f * sd.dtds[2u];
940
941 matrix_type<3, 3> dBdr_ini = evaluate_field_gradient(stepping, r_ini);
942 matrix_type<3, 3> dBdr_mid = evaluate_field_gradient(stepping, r_mid);
943 matrix_type<3, 3> dBdr_fin = evaluate_field_gradient(stepping, r_fin);
944
945 /*-----------------------------------------------------------------
946 * Calculate all terms of dk_n/dr1
947 -------------------------------------------------------------------*/
948 const auto I33 = matrix::identity<matrix_type<3, 3>>();
949
950 // dk1/dr1
951 dkndr[0u] = -sd.qop[0u] * matrix::column_wise_cross(dBdr_ini, sd.t[0u]);
952
953 const auto dkndr0_tmp = (I33 + h2 * 0.125f * dkndr[0u]);
954
955 // dk2/dr1
956 dkndr[1u] = sd.qop[1u] *
957 matrix::column_wise_cross(half_h * dkndr[0u], sd.b_middle);
958 dkndr[1u] =
959 dkndr[1u] - sd.qop[1u] * matrix::column_wise_cross(
960 dBdr_mid * dkndr0_tmp, sd.t[1u]);
961
962 // dk3/dr1
963 dkndr[2u] = sd.qop[2u] *
964 matrix::column_wise_cross(half_h * dkndr[1u], sd.b_middle);
965 dkndr[2u] =
966 dkndr[2u] - sd.qop[2u] * matrix::column_wise_cross(
967 dBdr_mid * dkndr0_tmp, sd.t[2u]);
968
969 // dk4/dr1
970 dkndr[3u] =
971 sd.qop[3u] * matrix::column_wise_cross(h * dkndr[2u], sd.b_last);
972 dkndr[3u] = dkndr[3u] -
973 sd.qop[3u] *
974 matrix::column_wise_cross(
975 dBdr_fin * (I33 + h2 * 0.5f * dkndr[2u]), sd.t[3u]);
976
977 // Set dF/dr1 and dG/dr1
978 dFdr = dFdr + h * h_6 * (dkndr[0u] + dkndr[1u] + dkndr[2u]);
979 dGdr = h_6 * (dkndr[0u] + 2.f * (dkndr[1u] + dkndr[2u]) + dkndr[3u]);
980 }
981 } else {
982 assert(!cfg.use_field_gradient);
983 }
984
985 const auto old_jacobian = stepping.internal_transport_jacobian();
986
987 if constexpr ((flags_v & static_cast<std::uint32_t>(
989 0u) {
990 detail::update_transport_jacobian_with_gradient_impl(
991 old_jacobian, dFdt, dGdt, dFdr, dGdr, dFdqop, dGdqop, dqopqop,
992 stepping.internal_transport_jacobian());
993 } else {
994 assert(!cfg.use_field_gradient);
995
996 detail::update_transport_jacobian_without_gradient_impl(
997 old_jacobian, dFdt, dGdt, dFdqop, dGdqop, dqopqop,
998 stepping.internal_transport_jacobian());
999 }
1000 }
1001
1006 DETRAY_HOST_DEVICE
1008 detray::rk_stepper<magnetic_field_t, algebra_t, constraint_t, policy_t,
1009 inspector_t, flags_v>::state& stepping,
1010 const point3_type& pos) const {
1011 auto dBdr = matrix::zero<matrix_type<3, 3>>();
1012
1013 constexpr auto delta{1e-1f * unit<scalar_type>::mm};
1014
1015 for (unsigned int i = 0; i < 3; i++) {
1016 point3_type dpos1 = pos;
1017 dpos1[i] += delta;
1018 const auto bvec1_tmp = stepping.field().at(dpos1[0], dpos1[1], dpos1[2]);
1019 vector3_type bvec1;
1020 bvec1[0u] = bvec1_tmp[0u];
1021 bvec1[1u] = bvec1_tmp[1u];
1022 bvec1[2u] = bvec1_tmp[2u];
1023
1024 point3_type dpos2 = pos;
1025 dpos2[i] -= delta;
1026 const auto bvec2_tmp = stepping.field().at(dpos2[0], dpos2[1], dpos2[2]);
1027 vector3_type bvec2;
1028 bvec2[0u] = bvec2_tmp[0u];
1029 bvec2[1u] = bvec2_tmp[1u];
1030 bvec2[2u] = bvec2_tmp[2u];
1031
1032 assert(delta != 0.f);
1033 const vector3_type gradient = (bvec1 - bvec2) * (1.f / (2.f * delta));
1034
1035 getter::element(dBdr, 0u, i) = gradient[0u];
1036 getter::element(dBdr, 1u, i) = gradient[1u];
1037 getter::element(dBdr, 2u, i) = gradient[2u];
1038 }
1039
1040 return dBdr;
1041 }
1042};
1043
1044} // namespace detray
Base stepper implementation.
Definition base_stepper.hpp:49
bound_track_parameters< algebra_t > bound_track_parameters_type
Definition base_stepper.hpp:57
free_track_parameters< algebra_t > free_track_parameters_type
Definition base_stepper.hpp:55
Runge-Kutta-Nystrom 4th order stepper implementation.
Definition rk_stepper.hpp:51
dscalar< algebra_t > scalar_type
Scalar type associated with the algebra backend.
Definition rk_stepper.hpp:64
void advance_jacobian(detray::rk_stepper< magnetic_field_t, algebra_t, constraint_t, policy_t, inspector_t, flags_v >::state &stepping, const stepping::config &cfg, const intermediate_state &sd, const material< scalar_type > *vol_mat_ptr) const
Update the jacobian transport from free propagation.
Definition rk_stepper.hpp:692
algebra_t algebra_type
Algebra backend used by this stepper.
Definition rk_stepper.hpp:62
std::conditional_t< uses_gradient, detail::transport_jacobian_matrix_with_gradient< algebra_type >, detail::transport_jacobian_matrix_without_gradient< algebra_type > > transport_jacobian_type
Transport Jacobian representation selected by the flags.
Definition rk_stepper.hpp:83
matrix_type< 3, 3 > evaluate_field_gradient(detray::rk_stepper< magnetic_field_t, algebra_t, constraint_t, policy_t, inspector_t, flags_v >::state &stepping, const point3_type &pos) const
Estimate the magnetic-field spatial gradient by centered differences.
Definition rk_stepper.hpp:1007
detray::pair< vector3_type, vector3_type > evaluate_dtds(detray::rk_stepper< magnetic_field_t, algebra_t, constraint_t, policy_t, inspector_t, flags_v >::state &stepping, const vector3_type &b_field, const std::size_t i, const scalar_type h, const vector3_type &dtds_prev, const scalar_type qop) const
Evaluate dtds for Runge-Kutta stepping.
Definition rk_stepper.hpp:624
static constexpr bool uses_gradient
True when field-gradient terms are available for transport.
Definition rk_stepper.hpp:57
dtransform3D< algebra_t > transform3_type
Three-dimensional transform type.
Definition rk_stepper.hpp:70
dpoint3D< algebra_t > point3_type
Three-dimensional point type.
Definition rk_stepper.hpp:66
dmatrix< algebra_t, ROWS, COLS > matrix_type
Matrix type with the requested dimensions.
Definition rk_stepper.hpp:81
detray::pair< scalar_type, scalar_type > evaluate_dqopds(detray::rk_stepper< magnetic_field_t, algebra_t, constraint_t, policy_t, inspector_t, flags_v >::state &stepping, const std::size_t i, const scalar_type h, const scalar_type dqopds_prev, const material< scalar_type > *vol_mat_ptr, const detray::stepping::config &cfg) const
evaluate dqopds for a given step size and material
Definition rk_stepper.hpp:582
dvector3D< algebra_t > vector3_type
Three-dimensional vector type.
Definition rk_stepper.hpp:68
typename base_type::bound_track_parameters_type bound_track_parameters_type
Bound track parameter type accepted by the stepper.
Definition rk_stepper.hpp:75
void advance_track(detray::rk_stepper< magnetic_field_t, algebra_t, constraint_t, policy_t, inspector_t, flags_v >::state &stepping, const intermediate_state &sd, const material< scalar_type > *vol_mat_ptr) const
Update the track state by Runge-Kutta-Nystrom integration.
Definition rk_stepper.hpp:651
magnetic_field_t magnetic_field_type
Magnetic field representation used by the stepper.
Definition rk_stepper.hpp:78
bool step(const scalar_type dist_to_next, state &stepping, const stepping::config &cfg, bool do_reset, const material< scalar_type > *vol_mat_ptr=nullptr) const
Take a step, using an adaptive Runge-Kutta algorithm.
Definition rk_stepper.hpp:320
typename base_type::free_track_parameters_type free_track_parameters_type
Free track parameter type advanced by the stepper.
Definition rk_stepper.hpp:72
Detray include(s).
Definition array_operators.hpp:18
get_scalar_t< A, T > dscalar
Scalar type for algebra A and value type T.
Definition algebra.hpp:64
std::array< value_t, kDIM > darray
Fixed-size array container.
Definition containers.hpp:30
tuple< T1, T2 > pair
A two-element tuple alias.
Definition tuple.hpp:145
get_vector3D_t< A, T > dvector3D
Three-dimensional vector type for algebra A and value type T.
Definition algebra.hpp:80
rk_stepper_flags
Optional capabilities enabled in an RK stepper.
Definition rk_stepper.hpp:26
@ e_allow_field_gradient
Allow use of magnetic-field gradients in Jacobian transport.
Definition rk_stepper.hpp:30
@ e_allow_covariance_transport
Allow transport of the track covariance.
Definition rk_stepper.hpp:28
constexpr detray::tuple< Ts &... > tie(Ts &...args)
Create a tuple of references to the supplied objects.
Definition tuple.hpp:211
get_transform3D_t< A, T > dtransform3D
Three-dimensional transform type for algebra A and value type T.
Definition algebra.hpp:84
get_matrix_t< A, R, C, T > dmatrix
R by C matrix type for algebra A and value type T.
Definition algebra.hpp:88
get_point3D_t< A, T > dpoint3D
Three-dimensional point type for algebra A and value type T.
Definition algebra.hpp:72
constexpr detray::pair< std::decay_t< T1 >, std::decay_t< T2 > > make_pair(T1 &&v1, T2 &&v2)
Construct a pair by decaying and forwarding its two arguments.
Definition tuple.hpp:230
Computes energy loss and multiple scattering in detector material.
Definition interaction.hpp:23
scalar_type compute_stopping_power(const detray::material< scalar_type > &mat, const pdg_particle< scalar_type > &ptc, const relativistic_quantities &rq) const
Sum collisional and radiative stopping-power contributions.
Definition interaction.hpp:36
scalar_type derive_stopping_power(const detray::material< scalar_type > &mat, const pdg_particle< scalar_type > &ptc, const relativistic_quantities &rq) const
Differentiate the total stopping power with respect to q/p.
Definition interaction.hpp:116
Material composition properties and density data used by interactions.
Definition material.hpp:36
Intermediate values evaluated at the RK integration stages.
Definition rk_stepper.hpp:91
vector3_type b_middle
Magnetic field at the two middle integration stages.
Definition rk_stepper.hpp:95
darray< scalar_type, 4u > dqopds
d(q/p)/ds
Definition rk_stepper.hpp:105
darray< vector3_type, 4u > t
t = tangential direction = dr/ds
Definition rk_stepper.hpp:99
darray< vector3_type, 4u > dtds
dt/ds = d^2r/ds^2 = q/p ( t X B )
Definition rk_stepper.hpp:103
vector3_type b_first
Magnetic field at the first integration stage.
Definition rk_stepper.hpp:93
vector3_type b_last
Magnetic field at the final integration stage.
Definition rk_stepper.hpp:97
darray< scalar_type, 4u > qop
q/p
Definition rk_stepper.hpp:101
Per-track integration state maintained by the stepper.
Definition rk_stepper.hpp:109
void run_inspector(const stepping::config &cfg, const char *message, const scalar_type dist, Args &&... args)
Call the stepping inspector.
Definition rk_stepper.hpp:283
scalar_type d2qopdsdqop(const scalar_type qop, const material< scalar_type > *vol_mat_ptr) const
Evaluate d(d(qop)/ds)dqop.
Definition rk_stepper.hpp:240
vector3_type dtds() const
Evaluate dtds, where t is the unit tangential direction.
Definition rk_stepper.hpp:156
state(const free_track_parameters_type &t, const magnetic_field_t &mag_field)
Construct from free parameters and a magnetic field.
Definition rk_stepper.hpp:121
typename base_type::template state< transport_jacobian_type > base_state
Base state holding common track and transport data.
Definition rk_stepper.hpp:114
scalar_type dqopds(const material< scalar_type > *vol_mat_ptr) const
Evaluate d(qop)/ds.
Definition rk_stepper.hpp:186
scalar_type dqopds(const scalar_type qop, const material< scalar_type > *vol_mat_ptr) const
Evaluate the charge-over-momentum derivative at a supplied value.
Definition rk_stepper.hpp:200
void dtds(const vector3_type &v)
Set dtds, where t is the unit tangential direction.
Definition rk_stepper.hpp:180
void dqopds(const scalar_type &s)
Set d(qop)/ds.
Definition rk_stepper.hpp:233
scalar_type next_step_size() const
Definition rk_stepper.hpp:151
state(const bound_track_parameters_type &bound_params, const magnetic_field_t &mag_field, const detector_t &det, const typename detector_t::geometry_context &ctx)
Construct from bound parameters, field, and detector geometry.
Definition rk_stepper.hpp:131
const magnetic_field_type & field() const
Definition rk_stepper.hpp:139
void set_next_step_size(const scalar_type step)
Set the next step size.
Definition rk_stepper.hpp:144
Configuration flags and limits consumed by a stepper.
Definition stepping_config.hpp:29
bool use_field_gradient
Use b field gradient in error propagation.
Definition stepping_config.hpp:46
bool use_mean_loss
Use mean energy loss (Bethe) if false, most probable energy loss (Landau) will be used.
Definition stepping_config.hpp:42
float rk_error_tol
Runge-Kutta numeric error tolerance.
Definition stepping_config.hpp:33
float min_stepsize
Minimum step size.
Definition stepping_config.hpp:31
std::size_t max_rk_updates
Maximum number of Runge-Kutta step trials.
Definition stepping_config.hpp:39
bool do_covariance_transport
Do covariance transport.
Definition stepping_config.hpp:48
bool use_eloss_gradient
Use eloss gradient in error propagation.
Definition stepping_config.hpp:44
static constexpr scalar_t um
Length, native unit mm.
Definition units.hpp:25
static constexpr scalar_t mm
Native length unit, the millimetre.
Definition units.hpp:27