File indexing completed on 2025-09-15 09:06:16
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "corecel/Assert.hh"
0010 #include "corecel/math/Algorithms.hh"
0011 #include "corecel/math/ArrayUtils.hh"
0012 #include "orange/OrangeData.hh"
0013 #include "orange/OrangeTypes.hh"
0014 #include "orange/SenseUtils.hh"
0015 #include "orange/detail/BIHEnclosingVolFinder.hh"
0016 #include "orange/detail/BIHIntersectingVolFinder.hh"
0017 #include "orange/surf/LocalSurfaceVisitor.hh"
0018
0019 #include "detail/InfixEvaluator.hh"
0020 #include "detail/LazySenseCalculator.hh"
0021 #include "detail/LogicEvaluator.hh"
0022 #include "detail/SurfaceFunctors.hh"
0023 #include "detail/Types.hh"
0024 #include "detail/Utils.hh"
0025
0026 namespace celeritas
0027 {
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 class SimpleUnitTracker
0038 {
0039 public:
0040
0041
0042 using ParamsRef = NativeCRef<OrangeParamsData>;
0043 using Initialization = detail::Initialization;
0044 using Intersection = detail::Intersection;
0045 using LocalState = detail::LocalState;
0046
0047
0048 public:
0049
0050 inline CELER_FUNCTION
0051 SimpleUnitTracker(ParamsRef const& params, SimpleUnitId id);
0052
0053
0054
0055
0056 CELER_FUNCTION LocalVolumeId::size_type num_volumes() const
0057 {
0058 return unit_record_.volumes.size();
0059 }
0060
0061
0062 CELER_FUNCTION LocalSurfaceId::size_type num_surfaces() const
0063 {
0064 return unit_record_.surfaces.size();
0065 }
0066
0067
0068 CELER_FUNCTION SimpleUnitRecord const& unit_record() const
0069 {
0070 return unit_record_;
0071 }
0072
0073
0074 inline CELER_FUNCTION DaughterId daughter(LocalVolumeId vol) const;
0075
0076
0077
0078
0079 inline CELER_FUNCTION Initialization
0080 initialize(LocalState const& state) const;
0081
0082
0083 inline CELER_FUNCTION Initialization
0084 cross_boundary(LocalState const& state) const;
0085
0086
0087 inline CELER_FUNCTION Intersection intersect(LocalState const& state) const;
0088
0089
0090 inline CELER_FUNCTION Intersection intersect(LocalState const& state,
0091 real_type max_dist) const;
0092
0093
0094 inline CELER_FUNCTION real_type safety(Real3 const& pos,
0095 LocalVolumeId vol) const;
0096
0097
0098 inline CELER_FUNCTION Real3 normal(Real3 const& pos,
0099 LocalSurfaceId surf) const;
0100
0101 private:
0102
0103
0104 ParamsRef const& params_;
0105 SimpleUnitRecord const& unit_record_;
0106
0107
0108
0109
0110 inline CELER_FUNCTION LdgSpan<LocalVolumeId const>
0111 get_neighbors(LocalSurfaceId) const;
0112
0113 template<class F>
0114 inline CELER_FUNCTION LocalVolumeId find_volume_where(Real3 const& pos,
0115 F&& predicate) const;
0116
0117 template<class F>
0118 inline CELER_FUNCTION Intersection intersect_impl(LocalState const&,
0119 F&&) const;
0120
0121 inline CELER_FUNCTION Intersection simple_intersect(LocalState const&,
0122 VolumeView const&,
0123 size_type) const;
0124 inline CELER_FUNCTION Intersection complex_intersect(LocalState const&,
0125 VolumeView const&,
0126 size_type,
0127 Sense,
0128 real_type) const;
0129 template<class F>
0130 inline CELER_FUNCTION Intersection background_intersect(LocalState const&,
0131 F&&) const;
0132
0133
0134 inline CELER_FUNCTION LocalSurfaceVisitor make_surface_visitor() const;
0135
0136
0137 inline CELER_FUNCTION VolumeView
0138 make_local_volume(LocalVolumeId vol_id) const;
0139 };
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151 CELER_FUNCTION
0152 SimpleUnitTracker::SimpleUnitTracker(ParamsRef const& params, SimpleUnitId suid)
0153 : params_(params), unit_record_(params.simple_units[suid])
0154 {
0155 CELER_EXPECT(params_);
0156 }
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169 CELER_FUNCTION auto
0170 SimpleUnitTracker::initialize(LocalState const& state) const -> Initialization
0171 {
0172 CELER_EXPECT(params_);
0173 CELER_EXPECT(!state.surface && !state.volume);
0174
0175
0176
0177 detail::OnFace on_surface;
0178 auto is_inside = [this, &state, &on_surface](LocalVolumeId id) -> bool {
0179 VolumeView vol = this->make_local_volume(id);
0180 auto calc_senses = detail::LazySenseCalculator(
0181 this->make_surface_visitor(), vol, state.pos, on_surface);
0182 return detail::LogicEvaluator(vol.logic())(calc_senses);
0183 };
0184 LocalVolumeId id = this->find_volume_where(state.pos, is_inside);
0185
0186 if (on_surface)
0187 {
0188
0189 id = {};
0190 }
0191 else if (!id)
0192 {
0193
0194 id = unit_record_.background;
0195 }
0196
0197 return Initialization{id, {}};
0198 }
0199
0200
0201
0202
0203
0204 CELER_FUNCTION auto
0205 SimpleUnitTracker::cross_boundary(LocalState const& state) const
0206 -> Initialization
0207 {
0208 CELER_EXPECT(state.surface && state.volume);
0209
0210 detail::OnLocalSurface on_surface;
0211 auto is_inside
0212 = [this, &state, &on_surface](LocalVolumeId const& id) -> bool {
0213 if (id == state.volume)
0214 {
0215
0216 return false;
0217 }
0218
0219 VolumeView vol = this->make_local_volume(id);
0220 detail::OnFace face{detail::find_face(vol, state.surface)};
0221 auto calc_senses = detail::LazySenseCalculator(
0222 this->make_surface_visitor(), vol, state.pos, face);
0223
0224 if (detail::LogicEvaluator(vol.logic())(calc_senses))
0225 {
0226
0227 on_surface = get_surface(vol, face);
0228 return true;
0229 }
0230 return false;
0231 };
0232
0233 auto neighbors = this->get_neighbors(state.surface.id());
0234
0235
0236
0237 if (neighbors.size() < 3)
0238 {
0239 for (LocalVolumeId id : neighbors)
0240 {
0241 if (is_inside(id))
0242 {
0243 return {id, on_surface};
0244 }
0245 }
0246 }
0247 else
0248 {
0249 if (LocalVolumeId id = this->find_volume_where(state.pos, is_inside))
0250 {
0251 return {id, on_surface};
0252 }
0253 }
0254
0255 return {unit_record_.background, state.surface};
0256 }
0257
0258
0259
0260
0261
0262 CELER_FUNCTION auto SimpleUnitTracker::intersect(LocalState const& state) const
0263 -> Intersection
0264 {
0265 Intersection result = this->intersect_impl(state, detail::IsFinite{});
0266 return result;
0267 }
0268
0269
0270
0271
0272
0273 CELER_FUNCTION auto
0274 SimpleUnitTracker::intersect(LocalState const& state, real_type max_dist) const
0275 -> Intersection
0276 {
0277 CELER_EXPECT(max_dist > 0);
0278 Intersection result
0279 = this->intersect_impl(state, detail::IsNotFurtherThan{max_dist});
0280 if (!result)
0281 {
0282 result.distance = max_dist;
0283 }
0284 return result;
0285 }
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298 CELER_FUNCTION real_type SimpleUnitTracker::safety(Real3 const& pos,
0299 LocalVolumeId vol_id) const
0300 {
0301 CELER_EXPECT(vol_id);
0302
0303 VolumeView vol = this->make_local_volume(vol_id);
0304 if (!vol.simple_safety())
0305 {
0306
0307
0308 return 0;
0309 }
0310
0311
0312 real_type result = numeric_limits<real_type>::infinity();
0313 LocalSurfaceVisitor visit_surface(params_, unit_record_.surfaces);
0314 detail::CalcSafetyDistance calc_safety{pos};
0315 for (LocalSurfaceId surface : vol.faces())
0316 {
0317 result = celeritas::min(result, visit_surface(calc_safety, surface));
0318 }
0319
0320 CELER_ENSURE(result >= 0);
0321 return result;
0322 }
0323
0324
0325
0326
0327
0328 CELER_FUNCTION auto
0329 SimpleUnitTracker::normal(Real3 const& pos, LocalSurfaceId surf) const -> Real3
0330 {
0331 CELER_EXPECT(surf);
0332
0333 LocalSurfaceVisitor visit_surface(params_, unit_record_.surfaces);
0334 return visit_surface(detail::CalcNormal{pos}, surf);
0335 }
0336
0337
0338
0339
0340
0341
0342
0343 CELER_FUNCTION auto SimpleUnitTracker::get_neighbors(LocalSurfaceId surf) const
0344 -> LdgSpan<LocalVolumeId const>
0345 {
0346 CELER_EXPECT(surf < this->num_surfaces());
0347
0348 OpaqueId<ConnectivityRecord> conn_id
0349 = unit_record_.connectivity[surf.unchecked_get()];
0350 ConnectivityRecord const& conn = params_.connectivity_records[conn_id];
0351
0352 CELER_ENSURE(!conn.neighbors.empty());
0353 return params_.local_volume_ids[conn.neighbors];
0354 }
0355
0356
0357
0358
0359
0360
0361
0362 template<class F>
0363 CELER_FUNCTION LocalVolumeId
0364 SimpleUnitTracker::find_volume_where(Real3 const& pos, F&& predicate) const
0365 {
0366 detail::BIHEnclosingVolFinder find_volume{unit_record_.bih_tree,
0367 params_.bih_tree_data};
0368 return find_volume(pos, predicate);
0369 }
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392
0393 template<class F>
0394 CELER_FUNCTION auto
0395 SimpleUnitTracker::intersect_impl(LocalState const& state, F&& is_valid) const
0396 -> Intersection
0397 {
0398 CELER_EXPECT(state.volume && !state.temp_sense.empty());
0399
0400
0401 VolumeView vol = this->make_local_volume(state.volume);
0402 CELER_ASSERT(state.temp_next.size >= vol.max_intersections());
0403
0404 if (vol.implicit_vol())
0405 {
0406
0407 return this->background_intersect(state, is_valid);
0408 }
0409
0410
0411
0412
0413 detail::CalcIntersections calc_intersections{
0414 celeritas::forward<F>(is_valid),
0415 state.pos,
0416 state.dir,
0417 state.surface ? vol.find_face(state.surface.id()) : FaceId{},
0418 vol.simple_intersection(),
0419 state.temp_next};
0420 LocalSurfaceVisitor visit_surface(params_, unit_record_.surfaces);
0421 for (LocalSurfaceId surface : vol.faces())
0422 {
0423 visit_surface(calc_intersections, surface);
0424 }
0425 CELER_ASSERT(calc_intersections.face_idx() == vol.num_faces());
0426 size_type num_isect = calc_intersections.isect_idx();
0427 CELER_ASSERT(num_isect <= vol.max_intersections());
0428
0429 if (num_isect == 0)
0430 {
0431
0432
0433 return {};
0434 }
0435 else if (vol.simple_intersection())
0436 {
0437
0438
0439 return this->simple_intersect(state, vol, num_isect);
0440 }
0441 else if (vol.internal_surfaces())
0442 {
0443
0444
0445 celeritas::sort(state.temp_next.isect,
0446 state.temp_next.isect + num_isect,
0447 [&state](size_type a, size_type b) {
0448 return state.temp_next.distance[a]
0449 < state.temp_next.distance[b];
0450 });
0451
0452
0453 return this->complex_intersect(state,
0454 vol,
0455 num_isect,
0456 Sense::outside,
0457 numeric_limits<real_type>::infinity());
0458 }
0459
0460 CELER_ASSERT_UNREACHABLE();
0461 }
0462
0463
0464
0465
0466
0467 CELER_FUNCTION auto
0468 SimpleUnitTracker::simple_intersect(LocalState const& state,
0469 VolumeView const& vol,
0470 size_type num_isect) const -> Intersection
0471 {
0472 CELER_EXPECT(num_isect > 0);
0473
0474
0475
0476 size_type distance_idx
0477 = celeritas::min_element(state.temp_next.distance,
0478 state.temp_next.distance + num_isect,
0479 Less<real_type>{})
0480 - state.temp_next.distance;
0481 CELER_ASSERT(distance_idx < num_isect);
0482
0483
0484 LocalSurfaceId surface;
0485 {
0486 FaceId face = state.temp_next.face[distance_idx];
0487 CELER_ASSERT(face);
0488 surface = vol.get_surface(face);
0489 CELER_ASSERT(surface);
0490 }
0491
0492 Sense cur_sense;
0493 if (surface == state.surface.id())
0494 {
0495
0496
0497
0498 cur_sense = state.surface.sense();
0499 }
0500 else
0501 {
0502 LocalSurfaceVisitor visit_surface(params_, unit_record_.surfaces);
0503 SignedSense ss = visit_surface(detail::CalcSense{state.pos}, surface);
0504 CELER_ASSERT(ss != SignedSense::on);
0505 cur_sense = to_sense(ss);
0506 }
0507
0508
0509 Intersection result;
0510 result.surface = {surface, cur_sense};
0511 result.distance = state.temp_next.distance[distance_idx];
0512 return result;
0513 }
0514
0515
0516
0517
0518
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532 CELER_FUNCTION auto
0533 SimpleUnitTracker::complex_intersect(LocalState const& state,
0534 VolumeView const& vol,
0535 size_type num_isect,
0536 Sense target_sense,
0537 real_type max_search_dist) const
0538 -> Intersection
0539 {
0540 CELER_ASSERT(num_isect > 0);
0541
0542
0543
0544
0545
0546 Real3 pos{state.pos};
0547 detail::OnFace on_face(detail::find_face(vol, state.surface));
0548
0549
0550
0551 detail::LazySenseCalculator calc_sense{
0552 this->make_surface_visitor(), vol, pos, on_face};
0553
0554
0555
0556 detail::LogicEvaluator is_inside(vol.logic());
0557 CELER_ASSERT(is_inside(calc_sense) != (target_sense == Sense::inside));
0558
0559
0560 real_type previous_distance{0};
0561
0562
0563
0564
0565
0566 for (size_type isect_idx = 0; isect_idx != num_isect; ++isect_idx)
0567 {
0568
0569 size_type const isect = state.temp_next.isect[isect_idx];
0570 real_type const distance = state.temp_next.distance[isect];
0571
0572 if (distance >= max_search_dist)
0573 {
0574
0575 return {};
0576 }
0577
0578
0579 on_face = [&] {
0580 FaceId face{state.temp_next.face[isect]};
0581
0582
0583 return detail::OnFace{face, flip_sense(calc_sense(face))};
0584 }();
0585 axpy(distance - previous_distance, state.dir, &pos);
0586
0587
0588
0589 if (is_inside(calc_sense) == (target_sense == Sense::inside))
0590 {
0591
0592
0593
0594
0595 CELER_ENSURE(distance > 0 && !std::isinf(distance));
0596
0597 return {
0598 {vol.get_surface(on_face.id()), flip_sense(on_face.sense())},
0599 distance};
0600 }
0601 previous_distance = distance;
0602 }
0603
0604
0605
0606 return {};
0607 }
0608
0609
0610
0611
0612
0613
0614
0615 template<class F>
0616 CELER_FUNCTION auto
0617 SimpleUnitTracker::background_intersect(LocalState const& state,
0618 F&& is_valid) const -> Intersection
0619 {
0620 auto is_intersecting = [this, &state, &is_valid](
0621 LocalVolumeId vol_id,
0622 real_type max_search_dist) -> Intersection {
0623 VolumeView vol = this->make_local_volume(vol_id);
0624
0625 detail::CalcIntersections calc_intersections{
0626 is_valid,
0627 state.pos,
0628 state.dir,
0629 state.surface ? vol.find_face(state.surface.id()) : FaceId{},
0630 false,
0631 state.temp_next};
0632
0633 LocalSurfaceVisitor visit_surface(params_, unit_record_.surfaces);
0634 for (LocalSurfaceId surface : vol.faces())
0635 {
0636 visit_surface(calc_intersections, surface);
0637 }
0638
0639 size_type num_isect = calc_intersections.isect_idx();
0640 if (num_isect == 0)
0641 {
0642
0643 return {};
0644 }
0645
0646
0647 celeritas::sort(state.temp_next.isect,
0648 state.temp_next.isect + num_isect,
0649 [&state](size_type a, size_type b) {
0650 return state.temp_next.distance[a]
0651 < state.temp_next.distance[b];
0652 });
0653
0654
0655
0656 return this->complex_intersect(
0657 state, vol, num_isect, Sense::inside, max_search_dist);
0658 };
0659
0660 detail::BIHIntersectingVolFinder find_intersection{unit_record_.bih_tree,
0661 params_.bih_tree_data};
0662
0663 return find_intersection({state.pos, state.dir}, is_intersecting);
0664 }
0665
0666
0667
0668
0669
0670 CELER_FORCEINLINE_FUNCTION LocalSurfaceVisitor
0671 SimpleUnitTracker::make_surface_visitor() const
0672 {
0673 return LocalSurfaceVisitor{params_, unit_record_.surfaces};
0674 }
0675
0676
0677
0678
0679
0680 CELER_FORCEINLINE_FUNCTION VolumeView
0681 SimpleUnitTracker::make_local_volume(LocalVolumeId vol_id) const
0682 {
0683 return VolumeView{params_, unit_record_, vol_id};
0684 }
0685
0686
0687
0688
0689
0690 CELER_FUNCTION DaughterId SimpleUnitTracker::daughter(LocalVolumeId vol) const
0691 {
0692 CELER_EXPECT(vol < unit_record_.volumes.size());
0693 return params_.volume_records[unit_record_.volumes[vol]].daughter_id;
0694 }
0695
0696
0697 }