diff --git a/scripts/cmake-presets/goldfinger.json b/scripts/cmake-presets/goldfinger.json index fbb9ce3494..66f178f07c 100644 --- a/scripts/cmake-presets/goldfinger.json +++ b/scripts/cmake-presets/goldfinger.json @@ -43,7 +43,7 @@ "cacheVariables": { "CELERITAS_USE_SWIG": {"type": "BOOL", "value": "ON"}, "CMAKE_SWIG_CXX_FLAGS": "-Wno-unused-parameter;-Wno-deprecated-declarations", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/install" + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install", "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"} } }, diff --git a/src/orange/CMakeLists.txt b/src/orange/CMakeLists.txt index 1eb136bf13..1386953c09 100644 --- a/src/orange/CMakeLists.txt +++ b/src/orange/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND SOURCES construct/CsgTypes.cc construct/CsgTreeUtils.cc construct/DepthCalculator.cc + construct/detail/LocalSurfaceInserter.cc construct/detail/NodeSimplifier.cc detail/BIHBuilder.cc detail/BIHPartitioner.cc @@ -31,6 +32,7 @@ list(APPEND SOURCES detail/UniverseInserter.cc surf/ConeAligned.cc surf/CylAligned.cc + surf/FaceNamer.cc surf/GeneralQuadric.cc surf/Plane.cc surf/PlaneAligned.cc diff --git a/src/orange/construct/OrangeInput.hh b/src/orange/construct/OrangeInput.hh index f277f439d9..b55323db10 100644 --- a/src/orange/construct/OrangeInput.hh +++ b/src/orange/construct/OrangeInput.hh @@ -54,7 +54,7 @@ struct VolumeInput //---------------------------------------------------------------------------// /*! - * Input definition a daughter universe embedded in a parent cell + * Input definition a daughter universe embedded in a parent cell. */ struct DaughterInput { @@ -65,6 +65,8 @@ struct DaughterInput //---------------------------------------------------------------------------// /*! * Input definition for a unit. + * + * \todo Add a CsgTree object and \c vector volumes; */ struct UnitInput { diff --git a/src/orange/construct/detail/LocalSurfaceInserter.cc b/src/orange/construct/detail/LocalSurfaceInserter.cc new file mode 100644 index 0000000000..a31558bbb6 --- /dev/null +++ b/src/orange/construct/detail/LocalSurfaceInserter.cc @@ -0,0 +1,49 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file orange/construct/detail/LocalSurfaceInserter.cc +//---------------------------------------------------------------------------// +#include "LocalSurfaceInserter.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with defaults. + */ +LocalSurfaceInserter::LocalSurfaceInserter(VecSurface* v, + Tolerance<> const& tol) + : surfaces_{v}, soft_surface_equal_{tol} +{ + CELER_EXPECT(surfaces_); + CELER_EXPECT(surfaces_->empty()); +} + +//---------------------------------------------------------------------------// +/*! + * Look for duplicate surfaces and store equivalency relationship. + */ +LocalSurfaceId +LocalSurfaceInserter::merge_impl(LocalSurfaceId source, LocalSurfaceId target) +{ + CELER_EXPECT(source < surfaces_->size()); + CELER_EXPECT(target < source); + + if (auto iter = merged_.find(target); iter != merged_.end()) + { + // Surface was already merged with another: chain them + target = iter->second; + CELER_ENSURE(target < source); + } + + merged_.emplace(source, target); + return target; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/orange/construct/detail/LocalSurfaceInserter.hh b/src/orange/construct/detail/LocalSurfaceInserter.hh new file mode 100644 index 0000000000..1bcaa591df --- /dev/null +++ b/src/orange/construct/detail/LocalSurfaceInserter.hh @@ -0,0 +1,128 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file orange/construct/detail/LocalSurfaceInserter.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include +#include + +#include "orange/OrangeTypes.hh" +#include "orange/surf/SoftSurfaceEqual.hh" +#include "orange/surf/VariantSurface.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Merge local surfaces as they're being built. + * + * This will \em sometimes will return the ID of a previously inserted surface, + * and \em sometimes will push the surface onto the vector of existing ones. + * + * There are three cases to consider: + * - The new surface is entirely unique: we insert and return the new ID. + * - The surface is soft equivalent but not exactly like an existing surface: + * we insert but return an existing ID. + * - The surface is exactly the same: we do \em not insert, and return existing + * id. + * + * The second case adds the surface so that multiple nearby surfaces can be + * \em chained together, even if the tolerance between the furthest apart is + * greater than the soft equivalence tolerance. + */ +class LocalSurfaceInserter +{ + public: + //!@{ + //! \name Type aliases + using VecSurface = std::vector; + //!@} + + public: + // Construct with tolerance and a pointer to the surfaces vector + LocalSurfaceInserter(VecSurface* v, Tolerance<> const& tol); + + // Construct a surface with deduplication + template + inline LocalSurfaceId operator()(S const& surface); + + private: + //// TYPES //// + + using MapSurfId = std::unordered_map; + + //// DATA //// + + VecSurface* surfaces_; + SoftSurfaceEqual soft_surface_equal_; + ExactSurfaceEqual exact_surface_equal_; + MapSurfId merged_; + + //// METHODS //// + + LocalSurfaceId merge_impl(LocalSurfaceId source, LocalSurfaceId target); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct a surface with deduplication. + */ +template +LocalSurfaceId LocalSurfaceInserter::operator()(S const& source) +{ + VecSurface& all_surf = *surfaces_; + + // Test for soft equality against all existing surfaces + auto is_soft_equal = [this, &source](VariantSurface const& vtarget) { + if (S const* target = std::get_if(&vtarget)) + { + return soft_surface_equal_(source, *target); + } + return false; + }; + + // TODO: instead of linear search (making overall unit insertion + // quadratic!) accelerate by mapping surfaces into a hash and comparing all + // neighboring hash cells + auto iter = std::find_if(all_surf.begin(), all_surf.end(), is_soft_equal); + if (iter == all_surf.end()) + { + // Surface is completely unique + LocalSurfaceId result(all_surf.size()); + all_surf.emplace_back(std::in_place_type, source); + return result; + } + + // Surface is soft equivalent to an existing surface at this index + LocalSurfaceId target_id{static_cast(iter - all_surf.begin())}; + + CELER_ASSUME(std::holds_alternative(*iter)); + if (exact_surface_equal_(source, std::get(*iter))) + { + // Surfaces are *exactly* equal: don't insert + CELER_ENSURE(target_id < all_surf.size()); + return target_id; + } + + // Surface is a little bit different, so we still need to insert it + // to chain duplicates + LocalSurfaceId source_id(all_surf.size()); + all_surf.emplace_back(std::in_place_type, source); + + // Store the equivalency relationship and potentially chain equivalent + // surfaces + return this->merge_impl(source_id, target_id); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/orange/surf/FaceNamer.cc b/src/orange/surf/FaceNamer.cc new file mode 100644 index 0000000000..fee8f1de82 --- /dev/null +++ b/src/orange/surf/FaceNamer.cc @@ -0,0 +1,144 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file orange/surf/FaceNamer.cc +//---------------------------------------------------------------------------// +#include "corecel/Assert.hh" + +#include "FaceNamer.hh" +#include "VariantSurface.hh" + +namespace celeritas +{ +namespace +{ +//---------------------------------------------------------------------------// +constexpr char to_pm(Sense s) +{ + return s == Sense::inside ? 'p' : 'm'; +} + +#define ORANGE_INSTANTIATE_OP(IN) \ + template std::string FaceNamer::Impl::operator()(IN const&) \ + const; \ + template std::string FaceNamer::Impl::operator()(IN const&) \ + const; \ + template std::string FaceNamer::Impl::operator()(IN const&) const + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +/*! + * Apply to a surface with unknown type. + */ +std::string FaceNamer::operator()(Sense s, VariantSurface const& surf) +{ + CELER_ASSUME(!surf.valueless_by_exception()); + return std::visit(Impl{&state_, s}, surf); +} + +//---------------------------------------------------------------------------// +// IMPL DEFINITIONS +//---------------------------------------------------------------------------// +template +std::string FaceNamer::Impl::operator()(PlaneAligned const&) const +{ + return {to_pm(sense_), to_char(T)}; +} + +//! \cond +ORANGE_INSTANTIATE_OP(PlaneAligned); +//! \endcond + +//---------------------------------------------------------------------------// +/*! + * Construct a name for an along-axis cylinder. + */ +template +std::string FaceNamer::Impl::operator()(CylCentered const&) const +{ + return {'c', to_char(T)}; +} + +//! \cond +ORANGE_INSTANTIATE_OP(CylCentered); +//! \endcond + +//---------------------------------------------------------------------------// +/*! + * Construct a name for a centered sphere. + */ +std::string FaceNamer::Impl::operator()(SphereCentered const&) const +{ + return "s"; +} + +//---------------------------------------------------------------------------// +/*! + * Construct a name for an axis-aligned cylinder. + */ +template +std::string FaceNamer::Impl::operator()(CylAligned const&) const +{ + return {'c', to_char(T)}; +} + +//! \cond +ORANGE_INSTANTIATE_OP(CylAligned); +//! \endcond + +//---------------------------------------------------------------------------// +/*! + * Construct a name for a general plane. + */ +std::string FaceNamer::Impl::operator()(Plane const&) const +{ + return "p" + std::to_string(state_->num_planes_++); +} + +//---------------------------------------------------------------------------// +/*! + * Construct a name for a sphere. + */ +std::string FaceNamer::Impl::operator()(Sphere const&) const +{ + return "s"; +} + +//---------------------------------------------------------------------------// +/*! + * Construct a name for a cone. + */ +template +std::string FaceNamer::Impl::operator()(ConeAligned const&) const +{ + return {'k', to_char(T)}; +} + +//! \cond +ORANGE_INSTANTIATE_OP(ConeAligned); +//! \endcond + +//---------------------------------------------------------------------------// +/*! + * Construct a name for a simple quadric. + */ +std::string FaceNamer::Impl::operator()(SimpleQuadric const&) const +{ + return "sq"; +} + +//---------------------------------------------------------------------------// +/*! + * Construct a name for a quadric. + */ +std::string FaceNamer::Impl::operator()(GeneralQuadric const&) const +{ + return "gq"; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/orange/surf/FaceNamer.hh b/src/orange/surf/FaceNamer.hh new file mode 100644 index 0000000000..072f85960e --- /dev/null +++ b/src/orange/surf/FaceNamer.hh @@ -0,0 +1,93 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file orange/surf/FaceNamer.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "orange/OrangeTypes.hh" + +#include "SurfaceFwd.hh" +#include "VariantSurface.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Return a "name" for a face. + * + * This is generally a very short string to be used as an extension for a + * volume comprised of several intersecting surface half-spaces. Because + * successive surfaces should have separate names, this has a small internal + * state. + */ +class FaceNamer +{ + public: + // Construct with defaults + FaceNamer() = default; + + // Apply to a surface with known type + template + inline std::string operator()(Sense s, S const& surf); + + // Apply to a surface with unknown type + std::string operator()(Sense s, VariantSurface const& surf); + + private: + struct State + { + int num_planes_{0}; + }; + + // Persistent state + State state_; + + // Nested implementation class + struct Impl + { + State* state_; + Sense sense_; + + //// OPERATORS //// + + template + std::string operator()(PlaneAligned const&) const; + + template + std::string operator()(CylCentered const&) const; + + std::string operator()(SphereCentered const&) const; + + template + std::string operator()(CylAligned const&) const; + + std::string operator()(Plane const&) const; + + std::string operator()(Sphere const&) const; + + template + std::string operator()(ConeAligned const&) const; + + std::string operator()(SimpleQuadric const&) const; + + std::string operator()(GeneralQuadric const&) const; + }; +}; + +//---------------------------------------------------------------------------// +/*! + * Apply to a surface with known type. + */ +template +std::string FaceNamer::operator()(Sense s, S const& surf) +{ + return Impl{&state_, s}(surf); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/orange/surf/RecursiveSimplifier.hh b/src/orange/surf/RecursiveSimplifier.hh index d8c9c9bc96..4ab876f982 100644 --- a/src/orange/surf/RecursiveSimplifier.hh +++ b/src/orange/surf/RecursiveSimplifier.hh @@ -10,6 +10,8 @@ #include #include +#include "orange/OrangeTypes.hh" + #include "SurfaceSimplifier.hh" #include "VariantSurface.hh" #include "detail/AllSurfaces.hh" @@ -21,6 +23,9 @@ namespace celeritas /*! * Recursively simplify, then call the given function on the final surface. * + * The tolerance for this class should be "absolute" tolerance, i.e., the + * relative tolerance for an O(1) sized object. + * * Example: * \code RecursiveSimplifier print_simplified([](Sense s, auto&& surf) { @@ -43,6 +48,12 @@ class RecursiveSimplifier // Construct with tolerance and function to apply inline RecursiveSimplifier(F&& func, real_type tol); + //! Construct with tolerance and function to apply + RecursiveSimplifier(F&& func, Tolerance<> tol) + : RecursiveSimplifier(std::forward(func), tol.abs) + { + } + // Apply to a surface with a known type template void operator()(Sense s, S const& surf); diff --git a/src/orange/surf/SoftSurfaceEqual.hh b/src/orange/surf/SoftSurfaceEqual.hh index a96da3696c..bcb5011498 100644 --- a/src/orange/surf/SoftSurfaceEqual.hh +++ b/src/orange/surf/SoftSurfaceEqual.hh @@ -37,14 +37,17 @@ struct ExactSurfaceEqual class SoftSurfaceEqual { public: - //! Construct with tolerances - SoftSurfaceEqual(real_type rel, real_type abs) : soft_eq_{rel, abs} {} + // Construct with tolerance + inline SoftSurfaceEqual(Tolerance<> const& tol); //! Construct with relative tolerance only - explicit SoftSurfaceEqual(real_type rel) : soft_eq_{rel} {} + explicit SoftSurfaceEqual(real_type rel) + : SoftSurfaceEqual{Tolerance<>::from_relative(rel)} + { + } //! Construct with default tolerance - SoftSurfaceEqual() : soft_eq_{1e-10} {} + SoftSurfaceEqual() : SoftSurfaceEqual{Tolerance<>::from_default()} {} //// SURFACE FUNCTIONS //// @@ -71,7 +74,7 @@ class SoftSurfaceEqual bool operator()(GeneralQuadric const&, GeneralQuadric const&) const; private: - SoftEqual soft_eq_; + SoftEqual<> soft_eq_; bool soft_eq_sq(real_type a, real_type b) const; bool soft_eq_distance(Real3 const& a, Real3 const& b) const; @@ -79,6 +82,16 @@ class SoftSurfaceEqual //---------------------------------------------------------------------------// // INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with tolerance. + */ +SoftSurfaceEqual::SoftSurfaceEqual(Tolerance<> const& tol) + : soft_eq_{tol.rel, tol.abs} +{ + CELER_EXPECT(tol); +} + //---------------------------------------------------------------------------// /*! * Compare exact equality for two surfaces. diff --git a/src/orange/surf/Surfaces.hh b/src/orange/surf/Surfaces.hh deleted file mode 100644 index dc9c24d3fa..0000000000 --- a/src/orange/surf/Surfaces.hh +++ /dev/null @@ -1,106 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2021-2023 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file orange/surf/Surfaces.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "orange/OrangeData.hh" - -namespace celeritas -{ -//---------------------------------------------------------------------------// -/*! - * Access stored surface data. - * - * These are all surfaces in the entire geometry. - */ -class Surfaces -{ - public: - //!@{ - //! \name Type aliases - using ParamsRef = NativeCRef; - //!@} - - public: - // Construct with reference to persistent data - inline CELER_FUNCTION - Surfaces(ParamsRef const& params, SurfacesRecord const& surf_record); - - // Number of surfaces - inline CELER_FUNCTION LocalSurfaceId::size_type num_surfaces() const; - - // Get the type of an indexed surface - inline CELER_FUNCTION SurfaceType surface_type(LocalSurfaceId) const; - - // Convert a stored surface to a class - template - inline CELER_FUNCTION T make_surface(LocalSurfaceId) const; - - private: - ParamsRef const& params_; - SurfacesRecord const& surf_record_; -}; - -//---------------------------------------------------------------------------// -// INLINE DEFINITIONS -//---------------------------------------------------------------------------// -/*! - * Construct with reference to persistent data. - */ -CELER_FUNCTION -Surfaces::Surfaces(ParamsRef const& params, SurfacesRecord const& surf_record) - : params_(params), surf_record_(surf_record) -{ -} - -//---------------------------------------------------------------------------// -/*! - * Number of surfaces. - */ -CELER_FUNCTION LocalSurfaceId::size_type Surfaces::num_surfaces() const -{ - return surf_record_.size(); -} - -//---------------------------------------------------------------------------// -/*! - * Get the type of an indexed surface. - */ -CELER_FUNCTION SurfaceType Surfaces::surface_type(LocalSurfaceId sid) const -{ - CELER_EXPECT(sid < this->num_surfaces()); - OpaqueId offset = *surf_record_.types.begin(); - CELER_ASSERT(sid.unchecked_get() + offset.unchecked_get() - < params_.surface_types.size()); - return params_.surface_types[offset + sid.unchecked_get()]; -} - -//---------------------------------------------------------------------------// -/*! - * Convert a stored surface to a class. - */ -template -CELER_FUNCTION T Surfaces::make_surface(LocalSurfaceId sid) const -{ - static_assert(T::Storage::extent > 0, - "Template parameter must be a surface class"); - CELER_EXPECT(sid < surf_record_.data_offsets.size()); - CELER_EXPECT(this->surface_type(sid) == T::surface_type()); - - real_type const* data = params_.reals[AllItems{}].data(); - - OpaqueId start_offset - = params_.real_ids[surf_record_.data_offsets[sid.unchecked_get()]]; - auto stop_offset = start_offset.unchecked_get() - + static_cast(T::Storage::extent); - CELER_ASSERT(stop_offset <= params_.reals.size()); - return T{LdgSpan{data + start_offset.unchecked_get(), - data + stop_offset}}; -} - -//---------------------------------------------------------------------------// -} // namespace celeritas diff --git a/src/orange/surf/detail/RecursiveSimplifierImpl.hh b/src/orange/surf/detail/RecursiveSimplifierImpl.hh index bf99509ff8..418e07eb1e 100644 --- a/src/orange/surf/detail/RecursiveSimplifierImpl.hh +++ b/src/orange/surf/detail/RecursiveSimplifierImpl.hh @@ -25,8 +25,8 @@ class RecursiveSimplifierImpl { public: //! Construct with reference to function and values to be used - RecursiveSimplifierImpl(F& func, Sense sense, real_type tol) - : func_{func}, sense_{sense}, simplify_{&sense_, tol} + RecursiveSimplifierImpl(F& func, Sense sense, real_type abs_tol) + : func_{func}, sense_{sense}, simplify_{&sense_, abs_tol} { } diff --git a/src/orange/univ/SimpleUnitTracker.hh b/src/orange/univ/SimpleUnitTracker.hh index 2100ac969d..1acbd6f8a8 100644 --- a/src/orange/univ/SimpleUnitTracker.hh +++ b/src/orange/univ/SimpleUnitTracker.hh @@ -646,7 +646,7 @@ SimpleUnitTracker::background_intersect(LocalState const& state, //---------------------------------------------------------------------------// /*! - * Create a Surfaces object from the params for this unit. + * Create a surface visitor from the params for this unit. */ CELER_FORCEINLINE_FUNCTION LocalSurfaceVisitor SimpleUnitTracker::make_surface_visitor() const diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 84c398eba3..5ac89bc7eb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -216,9 +216,6 @@ celeritas_setup_tests(SERIAL PREFIX orange celeritas_add_test(orange/BoundingBox.test.cc LINK_LIBRARIES ${nlohmann_json_LIBRARIES}) celeritas_add_test(orange/BoundingBoxUtils.test.cc) -celeritas_add_test(orange/CsgTree.test.cc - LINK_LIBRARIES ${nlohmann_json_LIBRARIES}) -celeritas_add_test(orange/CsgTreeUtils.test.cc) celeritas_add_test(orange/MatrixUtils.test.cc) celeritas_add_test(orange/OrangeTypes.test.cc) celeritas_add_device_test(orange/Orange) @@ -229,6 +226,14 @@ celeritas_add_test(orange/detail/BIHTraverser.test.cc) celeritas_add_test(orange/detail/BIHUtils.test.cc) celeritas_add_test(orange/detail/UniverseIndexer.test.cc) +#-----------------------------------------------------------------------------# +# Construct +set(CELERITASTEST_PREFIX orange/construct) +celeritas_add_test(orange/construct/CsgTree.test.cc + LINK_LIBRARIES ${nlohmann_json_LIBRARIES}) +celeritas_add_test(orange/construct/CsgTreeUtils.test.cc) +celeritas_add_test(orange/construct/LocalSurfaceInserter.test.cc) + #-------------------------------------# # Transforms set(CELERITASTEST_PREFIX orange/transform) @@ -239,24 +244,26 @@ celeritas_add_test(orange/transform/VariantTransform.test.cc) #-------------------------------------# # Surfaces set(CELERITASTEST_PREFIX orange/surf) -celeritas_add_test(orange/surf/detail/QuadraticSolver.test.cc) celeritas_add_test(orange/surf/ConeAligned.test.cc) celeritas_add_test(orange/surf/CylAligned.test.cc) celeritas_add_test(orange/surf/CylCentered.test.cc) celeritas_add_test(orange/surf/GeneralQuadric.test.cc) celeritas_add_test(orange/surf/Plane.test.cc) celeritas_add_test(orange/surf/PlaneAligned.test.cc) -celeritas_add_test(orange/surf/RecursiveSimplifier.test.cc) celeritas_add_test(orange/surf/SimpleQuadric.test.cc) celeritas_add_test(orange/surf/Sphere.test.cc) celeritas_add_test(orange/surf/SphereCentered.test.cc) + +celeritas_add_test(orange/surf/FaceNamer.test.cc) +celeritas_add_test(orange/surf/RecursiveSimplifier.test.cc) celeritas_add_test(orange/surf/SoftSurfaceEqual.test.cc) celeritas_add_test(orange/surf/SurfaceClipper.test.cc) celeritas_add_test(orange/surf/SurfaceSimplifier.test.cc) -celeritas_add_test(orange/surf/VariantSurface.test.cc) +celeritas_add_device_test(orange/surf/LocalSurfaceVisitor) + +celeritas_add_test(orange/surf/detail/QuadraticSolver.test.cc) celeritas_add_test(orange/surf/detail/SurfaceTranslator.test.cc) celeritas_add_test(orange/surf/detail/SurfaceTransformer.test.cc) -celeritas_add_device_test(orange/surf/LocalSurfaceVisitor) #-------------------------------------# # Universe details diff --git a/test/orange/CsgTree.test.cc b/test/orange/construct/CsgTree.test.cc similarity index 99% rename from test/orange/CsgTree.test.cc rename to test/orange/construct/CsgTree.test.cc index 9e550de212..7b2353325c 100644 --- a/test/orange/CsgTree.test.cc +++ b/test/orange/construct/CsgTree.test.cc @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/CsgTree.test.cc +//! \file orange/construct/CsgTree.test.cc //---------------------------------------------------------------------------// #include "orange/construct/CsgTree.hh" diff --git a/test/orange/CsgTreeUtils.test.cc b/test/orange/construct/CsgTreeUtils.test.cc similarity index 99% rename from test/orange/CsgTreeUtils.test.cc rename to test/orange/construct/CsgTreeUtils.test.cc index 1a2401ba17..133d44dfde 100644 --- a/test/orange/CsgTreeUtils.test.cc +++ b/test/orange/construct/CsgTreeUtils.test.cc @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/CsgTreeUtils.test.cc +//! \file orange/construct/CsgTreeUtils.test.cc //---------------------------------------------------------------------------// #include "orange/construct/CsgTreeUtils.hh" diff --git a/test/orange/construct/LocalSurfaceInserter.test.cc b/test/orange/construct/LocalSurfaceInserter.test.cc new file mode 100644 index 0000000000..116e0cde3d --- /dev/null +++ b/test/orange/construct/LocalSurfaceInserter.test.cc @@ -0,0 +1,133 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file orange/construct/LocalSurfaceInserter.test.cc +//---------------------------------------------------------------------------// +#include "orange/construct/detail/LocalSurfaceInserter.hh" + +#include + +#include "corecel/sys/Stopwatch.hh" +#include "celeritas/random/distribution/UniformBoxDistribution.hh" +#include "celeritas/random/distribution/UniformRealDistribution.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace detail +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class LocalSurfaceInserterTest : public ::celeritas::test::Test +{ + protected: + static constexpr real_type small = 1e-5; // small < eps + static constexpr real_type eps = 1e-4; + static constexpr real_type large = 1e-3; // eps < large < sqrt(eps) + SoftSurfaceEqual softeq_{eps}; + + void SetUp() override + { + tol = Tolerance<>::from_relative(eps); + CELER_ENSURE(tol); + } + + LocalSurfaceInserter::VecSurface surfaces; + Tolerance<> tol; +}; + +TEST_F(LocalSurfaceInserterTest, no_duplicates) +{ + LocalSurfaceInserter insert(&surfaces, tol); + + EXPECT_EQ(0, insert(PlaneX{2.0}).unchecked_get()); + EXPECT_EQ(1, insert(PlaneY{2.0}).unchecked_get()); + EXPECT_EQ(2, insert(PlaneZ{2.0}).unchecked_get()); + EXPECT_EQ(3, surfaces.size()); +} + +TEST_F(LocalSurfaceInserterTest, exact_duplicates) +{ + LocalSurfaceInserter insert(&surfaces, tol); + + for (int i = 0; i < 3; ++i) + { + SCOPED_TRACE(i); + EXPECT_EQ(0, insert(PlaneX{2.0}).unchecked_get()); + EXPECT_EQ(1, insert(PlaneY{2.0}).unchecked_get()); + } + EXPECT_EQ(2, surfaces.size()); +} + +/*! + * Insert surfaces that are very close to each other. Because we keep the + * deduplicated but *not exactly equal* surfaces, the vector size grows. + */ +TEST_F(LocalSurfaceInserterTest, tiny_duplicates) +{ + LocalSurfaceInserter insert(&surfaces, tol); + + for (int i = 0; i < 3; ++i) + { + SCOPED_TRACE(i); + EXPECT_EQ(0, insert(PlaneX{2 + small * i}).unchecked_get()); + EXPECT_EQ(1, insert(PlaneY{2 + small * i}).unchecked_get()); + } + + EXPECT_EQ(6, insert(PlaneZ{2}).unchecked_get()); + EXPECT_EQ(7, surfaces.size()); +} + +/*! + * Insert surfaces that each have a gap of less than epsilon, but the + * first and third have a combined gap of *more*. This means insertion order + * changes the result, and could cause particles to be "lost" (need more than + * one bump) if jumping into a lower level. + */ +TEST_F(LocalSurfaceInserterTest, chained_duplicates) +{ + LocalSurfaceInserter insert(&surfaces, tol); + + EXPECT_EQ(0, insert(PlaneX{2}).unchecked_get()); + EXPECT_EQ(1, insert(PlaneY{2}).unchecked_get()); + + for (int i = 1; i < 4; ++i) + { + SCOPED_TRACE(i); + EXPECT_EQ(0, insert(PlaneX{2 + i * eps / 2}).unchecked_get()); + } + EXPECT_EQ(5, surfaces.size()); +} + +TEST_F(LocalSurfaceInserterTest, DISABLED_performance_test) +{ + std::mt19937 rng; + UniformRealDistribution<> sample_point{-1, 1}; + UniformBoxDistribution<> sample_box{{-1, -1, -1}, {1, 1, 1}}; + + for (int num_samples = 16; num_samples < 40000; num_samples *= 2) + { + cout << "Sampling " << num_samples << "..." << std::flush; + surfaces.reserve(num_samples * 2); + surfaces.clear(); + LocalSurfaceInserter insert(&surfaces, tol); + + Stopwatch get_time; + for (int i = 0; i < num_samples; ++i) + { + insert(Sphere(sample_box(rng), 1.0)); + insert(PlaneX(sample_point(rng))); + } + cout << get_time() << " s" << endl; + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace detail +} // namespace celeritas diff --git a/test/orange/surf/FaceNamer.test.cc b/test/orange/surf/FaceNamer.test.cc new file mode 100644 index 0000000000..887cfceb76 --- /dev/null +++ b/test/orange/surf/FaceNamer.test.cc @@ -0,0 +1,65 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file orange/surf/FaceNamer.test.cc +//---------------------------------------------------------------------------// +#include "orange/surf/FaceNamer.hh" + +#include "corecel/math/ArrayUtils.hh" +#include "orange/surf/VariantSurface.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class FaceNamerTest : public ::celeritas::test::Test +{ +}; + +TEST_F(FaceNamerTest, typed) +{ + constexpr auto in = Sense::inside; + constexpr auto out = Sense::outside; + + FaceNamer name_face; + + EXPECT_EQ("px", name_face(in, PlaneX(1))); + EXPECT_EQ("mx", name_face(out, PlaneX(1))); + EXPECT_EQ("px", name_face(in, PlaneX(2))); + + EXPECT_EQ("p0", name_face(in, Plane(make_unit_vector(Real3{1, 2, 3}), 1))); + EXPECT_EQ("p1", name_face(in, Plane(make_unit_vector(Real3{1, 2, 3}), -1))); + + EXPECT_EQ("cx", name_face(in, CCylX(5))); + EXPECT_EQ("cy", name_face(in, CCylY(6))); + EXPECT_EQ("cz", name_face(in, CCylZ(7))); + EXPECT_EQ("s", name_face(in, SphereCentered(1.0))); + EXPECT_EQ("cx", name_face(in, CylX({1, 2, 3}, 0.5))); + EXPECT_EQ("cy", name_face(in, CylY({1, 2, 3}, 0.6))); + EXPECT_EQ("cz", name_face(in, CylZ({1, 2, 3}, 0.7))); + EXPECT_EQ("s", name_face(in, Sphere({1, 2, 3}, 1.5))); + EXPECT_EQ("kx", name_face(in, ConeX({1, 2, 3}, 0.2))); + EXPECT_EQ("ky", name_face(in, ConeY({1, 2, 3}, 0.4))); + EXPECT_EQ("kz", name_face(in, ConeZ({1, 2, 3}, 0.6))); + EXPECT_EQ("sq", name_face(in, SimpleQuadric({0, 1, 2}, {6, 7, 8}, 9))); + EXPECT_EQ( + "gq", + name_face(in, GeneralQuadric({0, 1, 2}, {3, 4, 5}, {6, 7, 8}, 9))); +} + +TEST_F(FaceNamerTest, variant) +{ + VariantSurface cyl{std::in_place_type_t(), 6}; + + EXPECT_EQ("cy", FaceNamer{}(Sense::inside, cyl)); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/orange/surf/SoftSurfaceEqual.test.cc b/test/orange/surf/SoftSurfaceEqual.test.cc index 5007cf3d72..d955c34ae4 100644 --- a/test/orange/surf/SoftSurfaceEqual.test.cc +++ b/test/orange/surf/SoftSurfaceEqual.test.cc @@ -87,20 +87,16 @@ TEST_F(SoftSurfaceEqualTest, plane) Real3 const n = make_unit_vector(Real3{1, 1, 0}); Plane const ref{n, p}; - // Absolute length scales are used for comparing normals - real_type const smallabs = SoftEqual{eps}.abs() / 10; - real_type const largeabs = SoftEqual{eps}.abs() * 10; - if (CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) { EXPECT_TRUE(softeq_(ref, Plane{n, p + Real3{small, 0, 0}})); } EXPECT_FALSE(softeq_(ref, Plane{n, p + Real3{large, 0, 0}})); - Real3 const npert = make_unit_vector(n + Real3{smallabs, 0, 0}); + Real3 const npert = make_unit_vector(n + Real3{small, 0, 0}); EXPECT_TRUE(softeq_(ref, Plane{npert, p})); - Real3 const ndiff = make_unit_vector(n + Real3{0, largeabs, 0}); + Real3 const ndiff = make_unit_vector(n + Real3{0, large, 0}); EXPECT_FALSE(softeq_(ref, Plane{ndiff, p})); EXPECT_FALSE(softeq_(ref, Plane{make_unit_vector(Real3{-1, 1, 0}), p})); EXPECT_FALSE(softeq_(ref, Plane{make_unit_vector(Real3{1, -1, 0}), p})); diff --git a/test/orange/surf/VariantSurface.test.cc b/test/orange/surf/VariantSurface.test.cc deleted file mode 100644 index a6283048ab..0000000000 --- a/test/orange/surf/VariantSurface.test.cc +++ /dev/null @@ -1,56 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file orange/surf/VariantSurface.test.cc -//---------------------------------------------------------------------------// -#include "orange/surf/VariantSurface.hh" - -#include "celeritas_test.hh" - -namespace celeritas -{ -namespace test -{ -//---------------------------------------------------------------------------// - -class VariantSurfaceTest : public ::celeritas::test::Test -{ - protected: - void SetUp() override {} -}; - -TEST_F(VariantSurfaceTest, apply_translation) -{ - auto result = apply_transform(Translation{Real3{0, 1, 0}}, PlaneY{4}); - if (auto* py = std::get_if(&result)) - { - EXPECT_SOFT_EQ(5, py->position()); - } - else - { - FAIL() << "wrong type"; - } -} - -TEST_F(VariantSurfaceTest, apply_transformation) -{ - auto result = apply_transform( - Transformation{make_rotation(Axis::z, Turn{0.25}), Real3{1, 2, 0}}, - PlaneX{1}); - - if (auto* p = std::get_if(&result)) - { - EXPECT_VEC_SOFT_EQ((Real3{0, 1, 0}), p->normal()); - EXPECT_SOFT_EQ(3, p->displacement()); - } - else - { - FAIL() << "wrong type"; - } -} - -//---------------------------------------------------------------------------// -} // namespace test -} // namespace celeritas