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