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