96 grid.world = grid.manager->MakeBox(
"WORLD", medium, extent, extent, extent);
102 auto*
box = grid.manager->MakeBox(Form(
"cell_%d", copy), medium, halfBox, halfBox, halfBox);
111 grid.world->AddNode(grid.assembly, 1,
new TGeoTranslation(0., 0., 0.));
145 Grid grid = makeGrid(
"bbox_grid", 4, 3., 1.);
147 rootShape->ComputeBBox();
148 auto* shape =
new O2BVHAssembly(grid.assembly);
149 const auto* ours =
static_cast<const TGeoBBox*
>(shape);
150 const auto* theirs =
static_cast<const TGeoBBox*
>(rootShape);
154 for (
int axis = 0; axis < 3; ++axis) {
161 auto* manager =
new TGeoManager(
"empty_asm",
"empty_asm");
162 auto* medium = vacuum();
163 auto* world = manager->MakeBox(
"WORLD", medium, 10., 10., 10.);
164 auto* assembly =
new TGeoVolumeAssembly(
"EMPTY");
165 world->AddNode(assembly, 1,
new TGeoTranslation(0., 0., 0.));
166 manager->SetTopVolume(world);
167 auto* shape =
new O2BVHAssembly(assembly);
169 const double point[3] = {0., 0., 0.};
170 const double direction[3] = {1., 0., 0.};
172 BOOST_CHECK_EQUAL(shape->DistFromOutside(point, direction, 3, TGeoShape::Big()), TGeoShape::Big());
182 Grid grid = makeGrid(
"contains_grid", 6, 3., 1.);
183 auto* shape =
new O2BVHAssembly(grid.assembly);
184 const double reach = 1.3 * gridReach(grid);
187 for (
int trial = 0; trial < 20000; ++trial) {
188 const double point[3] = {rng.uniform(-reach, reach), rng.uniform(-reach, reach), rng.uniform(-reach, reach)};
189 clearNodeIndices(grid.assembly);
190 const bool fromBVH = shape->Contains(point);
191 const int bvhNode = grid.assembly->GetCurrentNodeIndex();
192 clearNodeIndices(grid.assembly);
193 const bool fromLoop = shape->Contains_Loop(point);
194 const int loopNode = grid.assembly->GetCurrentNodeIndex();
195 BOOST_REQUIRE_EQUAL(fromBVH, fromLoop);
196 BOOST_REQUIRE_EQUAL(bvhNode, loopNode);
197 inside += fromBVH ? 1 : 0;
200 BOOST_CHECK_GT(inside, 100);
201 BOOST_CHECK_LT(inside, 19900);
206 Grid grid = makeGrid(
"dist_grid", 6, 3., 1.);
207 auto* shape =
new O2BVHAssembly(grid.assembly);
208 const double start = 3. * gridReach(grid);
211 for (
int trial = 0; trial < 5000; ++trial) {
213 rng.direction(direction);
216 const double target[3] = {rng.uniform(-gridReach(grid), gridReach(grid)),
217 rng.uniform(-gridReach(grid), gridReach(grid)),
218 rng.uniform(-gridReach(grid), gridReach(grid))};
220 const double norm = std::sqrt(aim[0] * aim[0] + aim[1] * aim[1] + aim[2] * aim[2]);
221 for (
int axis = 0; axis < 3; ++axis) {
224 clearNodeIndices(grid.assembly);
225 const double fromBVH = shape->DistFromOutside(
origin, aim, 3, TGeoShape::Big());
226 const int bvhNode = grid.assembly->GetNextNodeIndex();
227 clearNodeIndices(grid.assembly);
228 const double fromLoop = shape->DistFromOutside_Loop(
origin, aim, TGeoShape::Big());
229 const int loopNode = grid.assembly->GetNextNodeIndex();
230 BOOST_REQUIRE_EQUAL(fromBVH, fromLoop);
231 BOOST_REQUIRE_EQUAL(bvhNode, loopNode);
232 hits += fromBVH < TGeoShape::Big() ? 1 : 0;
234 BOOST_CHECK_GT(hits, 1000);
239 Grid grid = makeGrid(
"step_grid", 5, 3., 1.);
240 auto* shape =
new O2BVHAssembly(grid.assembly);
241 const double origin[3] = {-50., 0., 0.};
242 const double direction[3] = {1., 0., 0.};
243 const double unbounded = shape->DistFromOutside(
origin, direction, 3, TGeoShape::Big());
244 BOOST_REQUIRE_LT(unbounded, TGeoShape::Big());
253 Grid grid = makeGrid(
"safety_grid", 6, 3., 1.);
254 auto* shape =
new O2BVHAssembly(grid.assembly);
255 const double reach = 1.5 * gridReach(grid);
258 for (
int trial = 0; trial < 4000; ++trial) {
259 const double point[3] = {rng.uniform(-reach, reach), rng.uniform(-reach, reach), rng.uniform(-reach, reach)};
260 const double fromBVH = shape->Safety(point, kFALSE);
261 const double fromLoop = shape->Safety_Loop(point, kFALSE);
262 BOOST_REQUIRE_EQUAL(fromBVH, fromLoop);
263 positive += fromBVH > 0. ? 1 : 0;
265 BOOST_CHECK_GT(positive, 100);
274 Grid grid = makeGrid(
"root_contains", 6, 3., 1.);
275 grid.manager->CloseGeometry();
276 BOOST_REQUIRE(grid.assembly->GetVoxels() !=
nullptr);
278 auto* shape =
new O2BVHAssembly(grid.assembly);
279 const double reach = 1.3 * gridReach(grid);
281 for (
int trial = 0; trial < 10000; ++trial) {
282 const double point[3] = {rng.uniform(-reach, reach), rng.uniform(-reach, reach), rng.uniform(-reach, reach)};
283 const bool fromRoot = rootShape->Contains(point);
284 const int rootNode = grid.assembly->GetCurrentNodeIndex();
285 const bool fromBVH = shape->Contains(point);
286 BOOST_REQUIRE_EQUAL(fromRoot, fromBVH);
288 BOOST_REQUIRE_EQUAL(
rootNode, grid.assembly->GetCurrentNodeIndex());
300 Grid grid = makeGrid(
"root_safety", 5, 3., 1.);
301 grid.manager->CloseGeometry();
303 auto* shape =
new O2BVHAssembly(grid.assembly);
304 const double reach = 1.5 * gridReach(grid);
306 int rootTooLarge = 0;
307 for (
int trial = 0; trial < 2000; ++trial) {
308 const double point[3] = {rng.uniform(-reach, reach), rng.uniform(-reach, reach), rng.uniform(-reach, reach)};
309 const double ours = shape->Safety(point, kFALSE);
310 const double theirs = rootShape->Safety(point, kFALSE);
311 BOOST_REQUIRE_EQUAL(ours, shape->Safety_Loop(point, kFALSE));
312 BOOST_REQUIRE_LE(ours, theirs);
313 rootTooLarge += theirs > ours ? 1 : 0;
315 BOOST_TEST_MESSAGE(
"ROOT returned more than the daughter minimum on " << rootTooLarge <<
" of 2000 points");
324 Grid grid = makeGrid(
"root_dist", 6, 3., 1.);
325 grid.manager->CloseGeometry();
326 BOOST_REQUIRE(grid.assembly->GetVoxels() !=
nullptr);
328 auto* shape =
new O2BVHAssembly(grid.assembly);
329 const double start = 3. * gridReach(grid);
333 for (
int trial = 0; trial < 2000; ++trial) {
335 rng.direction(direction);
337 const double target[3] = {rng.uniform(-gridReach(grid), gridReach(grid)),
338 rng.uniform(-gridReach(grid), gridReach(grid)),
339 rng.uniform(-gridReach(grid), gridReach(grid))};
341 const double norm = std::sqrt(aim[0] * aim[0] + aim[1] * aim[1] + aim[2] * aim[2]);
342 for (
int axis = 0; axis < 3; ++axis) {
345 const double ours = shape->DistFromOutside(
origin, aim, 3, TGeoShape::Big());
346 const double theirs = rootShape->DistFromOutside(
origin, aim, 3, TGeoShape::Big());
347 BOOST_REQUIRE_EQUAL(ours, shape->DistFromOutside_Loop(
origin, aim, TGeoShape::Big()));
348 if (theirs < TGeoShape::Big()) {
349 BOOST_REQUIRE_EQUAL(theirs, ours);
353 weFound += ours < TGeoShape::Big() ? 1 : 0;
355 BOOST_CHECK_GT(weFound, 500);
356 BOOST_TEST_MESSAGE(
"ROOT returned Big() on " << rootGaveUp <<
" of 2000 rays this class answered");
365 auto* manager =
new TGeoManager(
"overlap_asm",
"overlap_asm");
366 auto* medium = vacuum();
367 auto* world = manager->MakeBox(
"WORLD", medium, 50., 50., 50.);
368 auto* assembly =
new TGeoVolumeAssembly(
"OVERLAP");
371 auto*
box = manager->MakeBox(Form(
"ov_%d",
index), medium, 2., 2., 2.);
372 assembly->AddNode(
box,
index,
new TGeoTranslation(2. *
index, 0., 0.));
374 world->AddNode(assembly, 1,
new TGeoTranslation(0., 0., 0.));
375 manager->SetTopVolume(world);
376 auto* shape =
new O2BVHAssembly(assembly);
379 for (
int trial = 0; trial < 5000; ++trial) {
380 const double point[3] = {rng.uniform(-4., 12.), rng.uniform(-3., 3.), rng.uniform(-3., 3.)};
381 clearNodeIndices(assembly);
382 const bool fromBVH = shape->Contains(point);
383 const int bvhNode = assembly->GetCurrentNodeIndex();
384 clearNodeIndices(assembly);
385 const bool fromLoop = shape->Contains_Loop(point);
386 const int loopNode = assembly->GetCurrentNodeIndex();
387 BOOST_REQUIRE_EQUAL(fromBVH, fromLoop);
388 BOOST_REQUIRE_EQUAL(bvhNode, loopNode);
393 assembly->GetNode(
index)->MasterToLocal(point, local);
394 count += assembly->GetNode(
index)->GetVolume()->GetShape()->Contains(local) ? 1 : 0;
396 overlaps +=
count > 1 ? 1 : 0;
399 BOOST_CHECK_GT(overlaps, 100);
404 auto* manager =
new TGeoManager(
"nested_asm",
"nested_asm");
405 auto* medium = vacuum();
406 auto* world = manager->MakeBox(
"WORLD", medium, 100., 100., 100.);
407 auto* outer =
new TGeoVolumeAssembly(
"OUTER");
408 for (
int block = 0; block < 6; ++block) {
409 auto* inner =
new TGeoVolumeAssembly(Form(
"INNER_%d", block));
410 for (
int cell = 0; cell < 8; ++cell) {
411 auto*
box = manager->MakeBox(Form(
"n_%d_%d", block, cell), medium, 1., 1., 1.);
412 inner->AddNode(
box, cell,
new TGeoTranslation(2.5 * cell, 0., 0.));
414 auto* rotation =
new TGeoRotation(Form(
"rot_%d", block), 13. * block, 7. * block, 5. * block);
415 outer->AddNode(inner, block,
new TGeoCombiTrans(0., 6. * block, 0., rotation));
417 world->AddNode(outer, 1,
new TGeoTranslation(0., 0., 0.));
418 manager->SetTopVolume(world);
419 auto* shape =
new O2BVHAssembly(outer);
424 for (
int trial = 0; trial < 4000; ++trial) {
425 const double point[3] = {rng.uniform(-5., 25.), rng.uniform(-5., 35.), rng.uniform(-5., 5.)};
426 clearNodeIndices(outer);
427 const bool fromBVH = shape->Contains(point);
428 const int bvhNode = outer->GetCurrentNodeIndex();
429 clearNodeIndices(outer);
430 const bool fromLoop = shape->Contains_Loop(point);
431 BOOST_REQUIRE_EQUAL(fromBVH, fromLoop);
432 BOOST_REQUIRE_EQUAL(bvhNode, outer->GetCurrentNodeIndex());
433 inside += fromBVH ? 1 : 0;
436 rng.direction(direction);
437 const double origin[3] = {point[0] - 200. * direction[0], point[1] - 200. * direction[1],
438 point[2] - 200. * direction[2]};
439 const double distanceBVH = shape->DistFromOutside(
origin, direction, 3, TGeoShape::Big());
440 const double distanceLoop = shape->DistFromOutside_Loop(
origin, direction, TGeoShape::Big());
441 BOOST_REQUIRE_EQUAL(distanceBVH, distanceLoop);
442 hits += distanceBVH < TGeoShape::Big() ? 1 : 0;
444 BOOST_REQUIRE_EQUAL(shape->Safety(point, kFALSE), shape->Safety_Loop(point, kFALSE));
446 BOOST_CHECK_GT(inside, 50);
447 BOOST_CHECK_GT(hits, 500);
452 auto* manager =
new TGeoManager(
"rotated_asm",
"rotated_asm");
453 auto* medium = vacuum();
454 auto* world = manager->MakeBox(
"WORLD", medium, 100., 100., 100.);
455 auto* assembly =
new TGeoVolumeAssembly(
"ROTATED");
457 auto*
box = manager->MakeBox(Form(
"r_%d",
index), medium, 3., 0.5, 2.);
460 new TGeoCombiTrans(8. * std::cos(0.31 *
index), 8. * std::sin(0.31 *
index), 0.7 *
index - 14.,
463 world->AddNode(assembly, 1,
new TGeoTranslation(0., 0., 0.));
464 manager->SetTopVolume(world);
465 auto* shape =
new O2BVHAssembly(assembly);
467 for (
int trial = 0; trial < 4000; ++trial) {
468 const double point[3] = {rng.uniform(-15., 15.), rng.uniform(-15., 15.), rng.uniform(-20., 20.)};
469 BOOST_REQUIRE_EQUAL(shape->Contains(point), shape->Contains_Loop(point));
470 BOOST_REQUIRE_EQUAL(shape->Safety(point, kFALSE), shape->Safety_Loop(point, kFALSE));
472 rng.direction(direction);
473 BOOST_REQUIRE_EQUAL(shape->DistFromOutside(point, direction, 3, TGeoShape::Big()),
474 shape->DistFromOutside_Loop(point, direction, TGeoShape::Big()));
485 Grid grid = makeGrid(
"faces_grid", 5, 2., 1.);
486 auto* shape =
new O2BVHAssembly(grid.assembly);
487 const double first = -0.5 * grid.pitch * (grid.count - 1);
489 for (
int ix = 0; ix < grid.count; ++ix) {
490 for (
int iy = 0; iy < grid.count; ++iy) {
491 for (
int iz = 0; iz < grid.count; ++iz) {
493 const double point[3] = {
first + grid.pitch * ix + grid.halfBox,
first + grid.pitch * iy,
494 first + grid.pitch * iz};
495 clearNodeIndices(grid.assembly);
496 const bool fromBVH = shape->Contains(point);
497 const int bvhNode = grid.assembly->GetCurrentNodeIndex();
498 clearNodeIndices(grid.assembly);
499 BOOST_REQUIRE_EQUAL(fromBVH, shape->Contains_Loop(point));
500 BOOST_REQUIRE_EQUAL(bvhNode, grid.assembly->GetCurrentNodeIndex());
501 BOOST_REQUIRE_EQUAL(shape->Safety(point, kFALSE), shape->Safety_Loop(point, kFALSE));
511 Grid grid = makeGrid(
"seam_grid", 5, 2., 1.);
512 auto* shape =
new O2BVHAssembly(grid.assembly);
513 const double first = -0.5 * grid.pitch * (grid.count - 1);
514 const double start = 4. * gridReach(grid);
516 for (
int iy = 0; iy < grid.count; ++iy) {
517 for (
int iz = 0; iz < grid.count; ++iz) {
520 const double y =
first + grid.pitch * iy +
offset * grid.halfBox;
522 const double direction[3] = {1., 0., 0.};
523 clearNodeIndices(grid.assembly);
524 const double fromBVH = shape->DistFromOutside(
origin, direction, 3, TGeoShape::Big());
525 const int bvhNode = grid.assembly->GetNextNodeIndex();
526 clearNodeIndices(grid.assembly);
527 BOOST_REQUIRE_EQUAL(fromBVH, shape->DistFromOutside_Loop(
origin, direction, TGeoShape::Big()));
528 BOOST_REQUIRE_EQUAL(bvhNode, grid.assembly->GetNextNodeIndex());
538 Grid grid = makeGrid(
"axis_grid", 5, 3., 1.);
539 auto* shape =
new O2BVHAssembly(grid.assembly);
540 const double start = 4. * gridReach(grid);
541 const double first = -0.5 * grid.pitch * (grid.count - 1);
542 for (
int axis = 0; axis < 3; ++axis) {
543 for (
int step = 0; step < grid.count; ++step) {
544 double origin[3] = {0., 0., 0.};
545 double direction[3] = {0., 0., 0.};
547 direction[axis] = 1.;
548 origin[(axis + 1) % 3] =
first + grid.pitch * step;
549 const double fromBVH = shape->DistFromOutside(
origin, direction, 3, TGeoShape::Big());
550 BOOST_REQUIRE_EQUAL(fromBVH, shape->DistFromOutside_Loop(
origin, direction, TGeoShape::Big()));
551 BOOST_REQUIRE_LT(fromBVH, TGeoShape::Big());
562 auto* manager =
new TGeoManager(
"lazy_asm",
"lazy_asm");
563 auto* medium = vacuum();
564 auto* world = manager->MakeBox(
"WORLD", medium, 50., 50., 50.);
565 auto* assembly =
new TGeoVolumeAssembly(
"LAZY");
566 auto* firstBox = manager->MakeBox(
"lz_0", medium, 1., 1., 1.);
567 assembly->AddNode(firstBox, 0,
new TGeoTranslation(0., 0., 0.));
568 world->AddNode(assembly, 1,
new TGeoTranslation(0., 0., 0.));
569 manager->SetTopVolume(world);
571 auto* shape =
new O2BVHAssembly(assembly);
572 assembly->SetShape(shape);
573 const double newPoint[3] = {10., 0., 0.};
576 auto* secondBox = manager->MakeBox(
"lz_1", medium, 1., 1., 1.);
577 assembly->AddNode(secondBox, 1,
new TGeoTranslation(10., 0., 0.));
586 Grid grid = makeGrid(
"swap_asm", 5, 3., 1.);
587 grid.manager->CloseGeometry();
588 BOOST_REQUIRE(grid.assembly->GetVoxels() !=
nullptr);
589 auto* shape = O2BVHAssembly::MakeBVHAssembly(grid.assembly);
590 BOOST_REQUIRE(shape !=
nullptr);
597 BOOST_CHECK(grid.assembly->GetVoxels() !=
nullptr);
598 BOOST_CHECK(O2BVHAssembly::MakeBVHAssembly(
nullptr) ==
nullptr);
603 Grid grid = makeGrid(
"nav_asm", 5, 3., 1.);
604 grid.manager->CloseGeometry();
605 const double reach = 1.2 * gridReach(grid);
607 std::vector<double> points;
608 std::vector<std::string>
paths;
609 for (
int trial = 0; trial < 3000; ++trial) {
610 const double point[3] = {rng.uniform(-reach, reach), rng.uniform(-reach, reach), rng.uniform(-reach, reach)};
611 grid.manager->FindNode(point[0], point[1], point[2]);
612 points.insert(points.end(), {point[0], point[1], point[2]});
613 paths.emplace_back(grid.manager->GetPath());
615 O2BVHAssembly::MakeBVHAssembly(grid.assembly);
617 for (
size_t trial = 0; trial <
paths.size(); ++trial) {
618 grid.manager->FindNode(points[3 * trial], points[3 * trial + 1], points[3 * trial + 2]);
619 BOOST_REQUIRE_EQUAL(
paths[trial], std::string(grid.manager->GetPath()));
620 deep +=
paths[trial].find(
"/cell_") != std::string::npos ? 1 : 0;
622 BOOST_CHECK_GT(
deep, 100);
627 Grid
reference = makeGrid(
"transport_root", 5, 3., 1.);
631 std::vector<double> origins;
632 std::vector<double> directions;
633 std::vector<std::vector<std::string>> rootPaths;
634 for (
int ray = 0; ray < 200; ++ray) {
636 rng.direction(direction);
638 origins.insert(origins.end(), {origin[0], origin[1], origin[2]});
639 directions.insert(directions.end(), {direction[0], direction[1], direction[2]});
641 std::vector<std::string>
path;
643 while (!
reference.manager->IsOutside() && guard++ < 500) {
644 reference.manager->FindNextBoundaryAndStep(1.e10);
647 rootPaths.push_back(
path);
650 O2BVHAssembly::MakeBVHAssembly(
reference.assembly);
652 for (
int ray = 0; ray < 200; ++ray) {
653 reference.manager->InitTrack(&origins[3 * ray], &directions[3 * ray]);
654 std::vector<std::string>
path;
656 while (!
reference.manager->IsOutside() && guard++ < 500) {
657 reference.manager->FindNextBoundaryAndStep(1.e10);
662 BOOST_REQUIRE_GE(
path.size(), rootPaths[ray].size());
663 for (
const auto& step : rootPaths[ray]) {
664 crossings += step.find(
"/cell_") != std::string::npos ? 1 : 0;
666 if (
path.size() == rootPaths[ray].size()) {
667 BOOST_REQUIRE(
path == rootPaths[ray]);
670 BOOST_CHECK_GT(crossings, 100);
675 const std::string file =
"testBVHAssembly_roundtrip.root";
676 Grid grid = makeGrid(
"io_asm", 4, 3., 1.);
677 grid.manager->CloseGeometry();
678 O2BVHAssembly::MakeBVHAssembly(grid.assembly);
679 const double reach = 1.2 * gridReach(grid);
681 std::vector<double> points;
682 std::vector<int>
nodes;
683 auto* shape =
static_cast<O2BVHAssembly*
>(grid.assembly->GetShape());
684 for (
int trial = 0; trial < 2000; ++trial) {
685 const double point[3] = {rng.uniform(-reach, reach), rng.uniform(-reach, reach), rng.uniform(-reach, reach)};
686 points.insert(points.end(), {point[0], point[1], point[2]});
687 clearNodeIndices(grid.assembly);
688 shape->Contains(point);
689 nodes.push_back(grid.assembly->GetCurrentNodeIndex());
691 grid.manager->Export(file.c_str());
693 auto* reloaded = TGeoManager::Import(file.c_str());
694 BOOST_REQUIRE(reloaded !=
nullptr);
695 auto* reloadedAssembly =
dynamic_cast<TGeoVolumeAssembly*
>(reloaded->GetTopVolume()->GetNode(0)->GetVolume());
696 BOOST_REQUIRE(reloadedAssembly !=
nullptr);
697 auto* reloadedShape =
dynamic_cast<O2BVHAssembly*
>(reloadedAssembly->GetShape());
698 BOOST_REQUIRE(reloadedShape !=
nullptr);
699 for (
size_t trial = 0; trial <
nodes.size(); ++trial) {
700 clearNodeIndices(reloadedAssembly);
701 reloadedShape->Contains(&points[3 * trial]);
702 BOOST_REQUIRE_EQUAL(
nodes[trial], reloadedAssembly->GetCurrentNodeIndex());
705 std::error_code ignored;
706 std::filesystem::remove(file, ignored);