File indexing completed on 2025-09-16 08:57:15
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include <algorithm>
0010 #include <type_traits>
0011 #include <G4LogicalVolume.hh>
0012 #include <G4Navigator.hh>
0013 #include <G4TouchableHandle.hh>
0014 #include <G4TouchableHistory.hh>
0015 #include <G4VPhysicalVolume.hh>
0016
0017 #include "corecel/Macros.hh"
0018 #include "corecel/math/Algorithms.hh"
0019 #include "corecel/math/ArrayUtils.hh"
0020 #include "corecel/math/SoftEqual.hh"
0021 #include "geocel/Types.hh"
0022
0023 #include "Convert.hh"
0024 #include "GeantGeoData.hh"
0025
0026 namespace celeritas
0027 {
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 class GeantGeoTrackView
0043 {
0044 public:
0045
0046
0047 using Initializer_t = GeoTrackInitializer;
0048 using ParamsRef = NativeCRef<GeantGeoParamsData>;
0049 using StateRef = NativeRef<GeantGeoStateData>;
0050 using real_type = double;
0051 using Real3 = Array<real_type, 3>;
0052
0053
0054
0055 struct DetailedInitializer
0056 {
0057 GeantGeoTrackView const& other;
0058 Real3 dir;
0059 };
0060
0061 public:
0062
0063 inline GeantGeoTrackView(ParamsRef const& params,
0064 StateRef const& state,
0065 TrackSlotId tid);
0066
0067
0068 inline GeantGeoTrackView& operator=(Initializer_t const& init);
0069
0070 inline GeantGeoTrackView& operator=(DetailedInitializer const& init);
0071
0072
0073
0074
0075 static constexpr real_type extra_push()
0076 {
0077 return 1e-12 * lengthunits::millimeter;
0078 }
0079
0080
0081
0082
0083
0084 CELER_FORCEINLINE Real3 const& pos() const { return pos_; }
0085 CELER_FORCEINLINE Real3 const& dir() const { return dir_; }
0086
0087
0088
0089 inline VolumeId volume_id() const;
0090
0091 inline VolumeInstanceId volume_instance_id() const;
0092
0093 inline LevelId level() const;
0094
0095 inline void volume_instance_id(Span<VolumeInstanceId> levels) const;
0096
0097
0098
0099 SurfaceId surface_id() const { return {}; }
0100 SurfaceId next_surface_id() const { return {}; }
0101
0102
0103
0104 inline bool is_outside() const;
0105
0106 inline bool is_on_boundary() const;
0107
0108 CELER_FORCEINLINE bool failed() const { return false; }
0109
0110
0111 inline G4NavigationHistory const* nav_history() const;
0112
0113
0114
0115
0116 inline Propagation find_next_step();
0117
0118
0119 inline Propagation find_next_step(real_type max_step);
0120
0121
0122 inline real_type find_safety();
0123
0124
0125 inline CELER_FUNCTION real_type find_safety(real_type max_step);
0126
0127
0128 inline void move_to_boundary();
0129
0130
0131 inline void move_internal(real_type step);
0132
0133
0134 inline void move_internal(Real3 const& pos);
0135
0136
0137 inline void cross_boundary();
0138
0139
0140 inline void set_dir(Real3 const& newdir);
0141
0142 private:
0143
0144
0145
0146
0147 Real3& pos_;
0148 Real3& dir_;
0149 real_type& next_step_;
0150 real_type& safety_radius_;
0151 G4TouchableHandle& touch_handle_;
0152 G4Navigator& navi_;
0153
0154
0155
0156 G4ThreeVector g4pos_;
0157 G4ThreeVector g4dir_;
0158 real_type g4safety_;
0159
0160
0161
0162
0163 inline bool has_next_step() const;
0164
0165
0166 inline G4LogicalVolume const* volume() const;
0167 };
0168
0169
0170
0171
0172
0173
0174
0175 GeantGeoTrackView::GeantGeoTrackView(ParamsRef const&,
0176 StateRef const& states,
0177 TrackSlotId tid)
0178 : pos_(states.pos[tid])
0179 , dir_(states.dir[tid])
0180 , next_step_(states.next_step[tid])
0181 , safety_radius_(states.safety_radius[tid])
0182 , touch_handle_(states.nav_state.touch_handle(tid))
0183 , navi_(states.nav_state.navigator(tid))
0184 , g4pos_(convert_to_geant(pos_, clhep_length))
0185 , g4dir_(convert_to_geant(dir_, 1))
0186 , g4safety_(convert_to_geant(safety_radius_, clhep_length))
0187 {
0188 }
0189
0190
0191
0192
0193
0194 GeantGeoTrackView& GeantGeoTrackView::operator=(Initializer_t const& init)
0195 {
0196 CELER_EXPECT(is_soft_unit_vector(init.dir));
0197
0198
0199 std::copy(init.pos.begin(), init.pos.end(), pos_.begin());
0200 std::copy(init.dir.begin(), init.dir.end(), dir_.begin());
0201 next_step_ = 0;
0202 safety_radius_ = -1;
0203
0204 g4pos_ = convert_to_geant(pos_, clhep_length);
0205 g4dir_ = convert_to_geant(dir_, 1);
0206 g4safety_ = -1;
0207
0208 navi_.LocateGlobalPointAndUpdateTouchable(g4pos_,
0209 g4dir_,
0210 touch_handle_(),
0211 false);
0212
0213 CELER_ENSURE(!this->has_next_step());
0214 return *this;
0215 }
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225 GeantGeoTrackView& GeantGeoTrackView::operator=(DetailedInitializer const& init)
0226 {
0227 CELER_EXPECT(is_soft_unit_vector(init.dir));
0228
0229 if (this != &init.other)
0230 {
0231
0232 pos_ = init.other.pos_;
0233 safety_radius_ = init.other.safety_radius_;
0234 g4pos_ = init.other.g4pos_;
0235 g4dir_ = init.other.g4dir_;
0236 g4safety_ = init.other.g4safety_;
0237
0238
0239 touch_handle_ = init.other.touch_handle_;
0240 navi_.ResetHierarchyAndLocate(
0241 g4pos_, g4dir_, dynamic_cast<G4TouchableHistory&>(*touch_handle_()));
0242 }
0243
0244
0245 dir_ = init.dir;
0246 next_step_ = 0;
0247
0248 CELER_ENSURE(!this->has_next_step());
0249 return *this;
0250 }
0251
0252
0253
0254
0255
0256 VolumeId GeantGeoTrackView::volume_id() const
0257 {
0258 CELER_EXPECT(!this->is_outside());
0259 return id_cast<VolumeId>(this->volume()->GetInstanceID());
0260 }
0261
0262
0263
0264
0265
0266 VolumeInstanceId GeantGeoTrackView::volume_instance_id() const
0267 {
0268 CELER_EXPECT(!this->is_outside());
0269 G4VPhysicalVolume* pv = touch_handle_()->GetVolume(0);
0270 if (!pv)
0271 return {};
0272 return id_cast<VolumeInstanceId>(pv->GetInstanceID());
0273 }
0274
0275
0276
0277
0278
0279 LevelId GeantGeoTrackView::level() const
0280 {
0281 auto* touch = touch_handle_();
0282 return id_cast<LevelId>(touch->GetHistoryDepth());
0283 }
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293 void GeantGeoTrackView::volume_instance_id(Span<VolumeInstanceId> levels) const
0294 {
0295 CELER_EXPECT(levels.size() == this->level().get() + 1);
0296
0297 auto* touch = touch_handle_();
0298 auto const max_depth = static_cast<size_type>(touch->GetHistoryDepth());
0299 for (auto lev : range(levels.size()))
0300 {
0301 G4VPhysicalVolume* pv = touch->GetVolume(max_depth - lev);
0302 levels[lev] = pv ? id_cast<VolumeInstanceId>(pv->GetInstanceID())
0303 : VolumeInstanceId{};
0304 }
0305 }
0306
0307
0308
0309
0310
0311 CELER_FORCEINLINE bool GeantGeoTrackView::is_outside() const
0312 {
0313 return this->volume() == nullptr;
0314 }
0315
0316
0317
0318
0319
0320 CELER_FORCEINLINE bool GeantGeoTrackView::is_on_boundary() const
0321 {
0322 return safety_radius_ == 0.0;
0323 }
0324
0325
0326
0327
0328
0329 G4NavigationHistory const* GeantGeoTrackView::nav_history() const
0330 {
0331 auto* touch = touch_handle_();
0332 CELER_ASSERT(touch);
0333 return touch->GetHistory();
0334 }
0335
0336
0337
0338
0339
0340 CELER_FORCEINLINE Propagation GeantGeoTrackView::find_next_step()
0341 {
0342 return this->find_next_step(numeric_limits<real_type>::infinity());
0343 }
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353 Propagation GeantGeoTrackView::find_next_step(real_type max_step)
0354 {
0355 CELER_EXPECT(!this->is_outside());
0356 CELER_EXPECT(max_step > 0);
0357
0358
0359 real_type g4step = convert_to_geant(max_step, clhep_length);
0360 g4step = navi_.ComputeStep(g4pos_, g4dir_, g4step, g4safety_);
0361
0362 if (g4safety_ != 0 && !this->is_on_boundary())
0363 {
0364
0365
0366 safety_radius_ = convert_from_geant(g4safety_, clhep_length);
0367 CELER_ASSERT(!this->is_on_boundary());
0368 }
0369
0370
0371 Propagation result;
0372 result.distance = convert_from_geant(g4step, clhep_length);
0373 if (result.distance <= max_step)
0374 {
0375 result.boundary = true;
0376 result.distance
0377 = celeritas::max<real_type>(result.distance, this->extra_push());
0378 CELER_ENSURE(result.distance > 0);
0379 }
0380 else
0381 {
0382
0383 result.distance = max_step;
0384 CELER_ENSURE(result.distance > 0);
0385 }
0386
0387
0388 next_step_ = result.distance;
0389
0390 CELER_ENSURE(result.distance > 0);
0391 CELER_ENSURE(result.distance <= max(max_step, this->extra_push()));
0392 CELER_ENSURE(result.boundary || result.distance == max_step
0393 || max_step < this->extra_push());
0394 CELER_ENSURE(this->has_next_step());
0395 return result;
0396 }
0397
0398
0399
0400
0401
0402 CELER_FORCEINLINE auto GeantGeoTrackView::find_safety() -> real_type
0403 {
0404 return this->find_safety(numeric_limits<real_type>::infinity());
0405 }
0406
0407
0408
0409
0410
0411
0412
0413
0414 auto GeantGeoTrackView::find_safety(real_type max_step) -> real_type
0415 {
0416 CELER_EXPECT(!this->is_on_boundary());
0417 CELER_EXPECT(max_step > 0);
0418 if (safety_radius_ < max_step)
0419 {
0420 real_type g4step = convert_to_geant(max_step, clhep_length);
0421 g4safety_ = navi_.ComputeSafety(g4pos_, g4step);
0422 safety_radius_ = max(convert_from_geant(g4safety_, clhep_length), 0.0);
0423 }
0424
0425 return safety_radius_;
0426 }
0427
0428
0429
0430
0431
0432 void GeantGeoTrackView::move_to_boundary()
0433 {
0434 CELER_EXPECT(this->has_next_step());
0435
0436
0437 axpy(next_step_, dir_, &pos_);
0438 axpy(convert_to_geant(next_step_, clhep_length), g4dir_, &g4pos_);
0439 next_step_ = 0;
0440 safety_radius_ = 0;
0441 g4safety_ = 0;
0442 navi_.SetGeometricallyLimitedStep();
0443
0444 CELER_ENSURE(this->is_on_boundary());
0445 }
0446
0447
0448
0449
0450
0451
0452
0453 void GeantGeoTrackView::cross_boundary()
0454 {
0455 CELER_EXPECT(this->is_on_boundary());
0456
0457 navi_.LocateGlobalPointAndUpdateTouchableHandle(
0458 g4pos_,
0459 g4dir_,
0460 touch_handle_,
0461 true);
0462
0463 CELER_ENSURE(this->is_on_boundary());
0464 }
0465
0466
0467
0468
0469
0470
0471
0472
0473 void GeantGeoTrackView::move_internal(real_type dist)
0474 {
0475 CELER_EXPECT(this->has_next_step());
0476 CELER_EXPECT(dist > 0 && dist <= next_step_);
0477
0478
0479 axpy(dist, dir_, &pos_);
0480 axpy(convert_to_geant(dist, clhep_length), g4dir_, &g4pos_);
0481 next_step_ -= dist;
0482 navi_.LocateGlobalPointWithinVolume(g4pos_);
0483
0484 safety_radius_ = -1;
0485 g4safety_ = 0;
0486 }
0487
0488
0489
0490
0491
0492
0493
0494
0495 void GeantGeoTrackView::move_internal(Real3 const& pos)
0496 {
0497 pos_ = pos;
0498 g4pos_ = convert_to_geant(pos_, clhep_length);
0499 next_step_ = 0;
0500 navi_.LocateGlobalPointWithinVolume(g4pos_);
0501
0502 safety_radius_ = -1;
0503 g4safety_ = 0;
0504 }
0505
0506
0507
0508
0509
0510
0511
0512
0513 void GeantGeoTrackView::set_dir(Real3 const& newdir)
0514 {
0515 CELER_EXPECT(is_soft_unit_vector(newdir));
0516 dir_ = newdir;
0517 g4dir_ = convert_to_geant(newdir, 1);
0518 next_step_ = 0;
0519 }
0520
0521
0522
0523
0524
0525
0526
0527 CELER_FORCEINLINE bool GeantGeoTrackView::has_next_step() const
0528 {
0529 return next_step_ != 0;
0530 }
0531
0532
0533
0534
0535
0536 auto GeantGeoTrackView::volume() const -> G4LogicalVolume const*
0537 {
0538 CELER_EXPECT(touch_handle_());
0539 G4VPhysicalVolume* pv = touch_handle_()->GetVolume(0);
0540 if (!pv)
0541 return nullptr;
0542
0543 return pv->GetLogicalVolume();
0544 }
0545
0546
0547 }