From 5646bd3ed621e641d72d23b2f8ab66f052dc61f5 Mon Sep 17 00:00:00 2001 From: Damien L-G Date: Tue, 16 Apr 2024 08:03:00 -0400 Subject: [PATCH 01/59] Set global execution timeout for automated testing on Jenkins (#1186) * Set global execution timeout of six hours on Jenkins Set a period timeout after which the Jenkins server will abort the pipeline run. Without it, a jobs that somehow gets stuck may run for days before it is manually killed by an admin. * Exclude jenkins modifications from github actions --- .github/workflows/pr.yml | 1 + .jenkins | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 60bf57ed7b..eb7abbabd0 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -12,6 +12,7 @@ on: - '**.rst' - '**.md' - 'scripts/dev' + - '.jenkins' concurrency: group: pr-${{github.ref}}-${{github.event.number}}-${{github.workflow}} diff --git a/.jenkins b/.jenkins index 9415b43c84..1957558cb7 100644 --- a/.jenkins +++ b/.jenkins @@ -2,6 +2,7 @@ pipeline { options { disableConcurrentBuilds(abortPrevious: true) newContainerPerStage() + timeout(time: 2, unit: 'HOURS') } triggers { issueCommentTrigger('.*do: test') From bbd64da885fdddfa2707c58076bc5f70ee02f0f0 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 16 Apr 2024 08:28:11 -0400 Subject: [PATCH 02/59] Construct proto-universe hierarchy from converted volumes (#1179) * Update pointer testing utils * Implement proto constructor * Add manual name construction to unit proto * Add interior box test * Construct 'fill' cells explicitly when there are 2 or less daughters * Clear isotopes and elements when resetting geometry * Turn on simple safety for orangeinp-created background --- src/geocel/GeantGeoUtils.cc | 27 + src/orange/CMakeLists.txt | 1 + src/orange/detail/UnitInserter.cc | 3 +- src/orange/g4org/ProtoConstructor.cc | 223 +++++++++ src/orange/g4org/ProtoConstructor.hh | 68 +++ src/orange/orangeinp/ProtoInterface.cc | 8 + src/orange/orangeinp/UnitProto.cc | 32 +- src/orange/orangeinp/UnitProto.hh | 27 +- test/Test.cc | 4 +- test/Test.hh | 4 +- test/geocel/data/testem3.gdml | 4 +- test/orange/CMakeLists.txt | 8 +- test/orange/Orange.test.cc | 4 +- .../data/inputbuilder-bgspheres.org.json | 2 +- .../data/inputbuilder-hierarchy.org.json | 10 +- test/orange/g4org/ProtoConstructor.test.cc | 472 ++++++++++++++++++ test/orange/orangeinp/CsgTestUtils.cc | 14 +- test/orange/orangeinp/UnitProto.test.cc | 162 +++--- 18 files changed, 961 insertions(+), 112 deletions(-) create mode 100644 src/orange/g4org/ProtoConstructor.cc create mode 100644 src/orange/g4org/ProtoConstructor.hh create mode 100644 test/orange/g4org/ProtoConstructor.test.cc diff --git a/src/geocel/GeantGeoUtils.cc b/src/geocel/GeantGeoUtils.cc index bcf93b03a2..d93d682114 100644 --- a/src/geocel/GeantGeoUtils.cc +++ b/src/geocel/GeantGeoUtils.cc @@ -7,14 +7,18 @@ //---------------------------------------------------------------------------// #include "GeantGeoUtils.hh" +#include #include #include #include #include +#include #include #include +#include #include #include +#include #include #include #include @@ -90,6 +94,25 @@ load_geant_geometry_impl(std::string const& filename, bool strip_pointer_ext) return result; } +//---------------------------------------------------------------------------// +/*! + * Free all pointers in a table. + * + * Geant4 requires "new"ing and *not* "delete"ing classes whose new/delete + * operators modify an entry in a global table. + */ +template +void free_and_clear(std::vector* table) +{ + for (auto* ptr : *table) + { + delete ptr; + } + CELER_ASSERT(std::all_of( + table->begin(), table->end(), [](T* ptr) { return ptr == nullptr; })); + table->clear(); +} + //---------------------------------------------------------------------------// } // namespace @@ -191,6 +214,10 @@ void reset_geant_geometry() #if G4VERSION_NUMBER >= 1100 G4ReflectionFactory::Instance()->Clean(); #endif + free_and_clear(G4Material::GetMaterialTable()); + free_and_clear(G4Element::GetElementTable()); + free_and_clear(const_cast*>( + G4Isotope::GetIsotopeTable())); msg = scoped_log.str(); } if (!msg.empty()) diff --git a/src/orange/CMakeLists.txt b/src/orange/CMakeLists.txt index 3078e30ec4..1d2098a55b 100644 --- a/src/orange/CMakeLists.txt +++ b/src/orange/CMakeLists.txt @@ -86,6 +86,7 @@ if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") set( _cg4org_sources g4org/LogicalVolumeConverter.cc g4org/PhysicalVolumeConverter.cc + g4org/ProtoConstructor.cc g4org/SolidConverter.cc ) celeritas_get_g4libs(_cg4org_libs geometry) diff --git a/src/orange/detail/UnitInserter.cc b/src/orange/detail/UnitInserter.cc index 3fc7119ae2..23ea3b6fd9 100644 --- a/src/orange/detail/UnitInserter.cc +++ b/src/orange/detail/UnitInserter.cc @@ -342,7 +342,8 @@ VolumeRecord UnitInserter::insert_volume(SurfacesRecord const& surf_record, std::end(nowhere_logic))); CELER_EXPECT(!v.bbox); CELER_EXPECT(v.flags & VolumeRecord::implicit_vol); - simple_safety = false; + // Rely on incoming flags for "simple_safety": false from .org.json, + // maybe true if built from GDML } VolumeRecord output; diff --git a/src/orange/g4org/ProtoConstructor.cc b/src/orange/g4org/ProtoConstructor.cc new file mode 100644 index 0000000000..0521ca646d --- /dev/null +++ b/src/orange/g4org/ProtoConstructor.cc @@ -0,0 +1,223 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/g4org/ProtoConstructor.cc +//---------------------------------------------------------------------------// +#include "ProtoConstructor.hh" + +#include + +#include "corecel/io/StreamableVariant.hh" +#include "orange/orangeinp/CsgObject.hh" +#include "orange/orangeinp/Transformed.hh" +#include "orange/transform/TransformIO.hh" + +#include "Volume.hh" + +namespace celeritas +{ +namespace g4org +{ +namespace +{ +//---------------------------------------------------------------------------// +using SPConstObject = ProtoConstructor::SPConstObject; + +//---------------------------------------------------------------------------// +/*! + * Construct an explicit "background" cell. + */ +SPConstObject make_explicit_background(LogicalVolume const& lv, + VariantTransform const& transform) +{ + using namespace orangeinp; + + std::vector children; + for (auto const& child_pv : lv.children) + { + auto child_transform = apply_transform(transform, child_pv.transform); + children.push_back( + Transformed::or_object(child_pv.lv->solid, child_transform)); + } + + return Transformed::or_object( + orangeinp::make_subtraction( + std::string{lv.name}, + lv.solid, + orangeinp::AnyObjects::or_object(lv.name + ".children", + std::move(children))), + transform); +} + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +/*! + * Construct a proto-universe from a logical volume. + */ +auto ProtoConstructor::operator()(LogicalVolume const& lv) -> SPUnitProto +{ + ProtoInput input; + input.boundary.interior = lv.solid; + input.label = lv.name; + + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << "* New proto: " << lv.name + << " with shape " << to_string(*lv.solid) << std::endl; + } + + // Add children first + for (PhysicalVolume const& pv : lv.children) + { + ++depth_; + this->place_pv(NoTransformation{}, pv, &input); + --depth_; + } + + // Heuristic: if LV has fewer than N daughters in the input, use an + // explicit background cell + if (lv.children.size() <= fill_daughter_threshold()) + { + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " - explicit background" + << std::endl; + } + // Create explicit "fill" for this logical volume + orangeinp::UnitProto::MaterialInput background; + background.interior = make_explicit_background(lv, NoTransformation{}); + background.label = Label::from_geant(lv.name); + background.fill = lv.material_id; + input.boundary.zorder = ZOrder::media; + input.materials.push_back(std::move(background)); + } + else + { + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " - implicit background" + << std::endl; + } + input.background.fill = lv.material_id; + input.background.label = Label::from_geant(lv.name); + } + + CELER_ENSURE(input); + return std::make_shared(std::move(input)); +} + +//---------------------------------------------------------------------------// +/*! + * Place this physical volume into the proto. + */ +void ProtoConstructor::place_pv(VariantTransform const& parent_transform, + PhysicalVolume const& pv, + ProtoInput* proto) +{ + CELER_EXPECT(proto); + + using namespace orangeinp; + + // Transform for this PV, whether as a "top level" volume or as a volume + // that's subtracted from an inlined LV + auto transform = apply_transform(parent_transform, pv.transform); + + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << "- Add pv " << pv.name + << " use_count=" << pv.lv.use_count() + << ", num_children=" << pv.lv->children.size() << ", at " + << StreamableVariant{transform} << " to " << proto->label + << std::endl; + } + + auto add_material = [proto, &lv = *pv.lv](SPConstObject&& obj) { + CELER_EXPECT(obj); + UnitProto::MaterialInput mat; + mat.interior = std::move(obj); + mat.fill = lv.material_id; + mat.label = Label::from_geant(lv.name); + proto->materials.push_back(std::move(mat)); + }; + + if (pv.lv->children.empty()) + { + // No children! This LV is just a material. + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " -> " + << "material at " << StreamableVariant{pv.transform} + << std::endl; + std::clog << std::string(depth_, ' ') << " " + << to_string(*pv.lv->solid) << std::endl; + } + + add_material( + Transformed::or_object(pv.lv->solid, std::move(transform))); + } + else if (pv.lv.use_count() == 1) + { + // Child can be inlined into the parent because it's used only once + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " -> " + << "inline the child at " + << StreamableVariant{pv.transform} << std::endl; + } + + // Subtract *its* children from this shape + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " : subtracted " + << pv.lv->children.size() << " children from " + << to_string(*pv.lv->solid) << std::endl; + } + + add_material(make_explicit_background(*pv.lv, transform)); + + // Now build its daghters + ++depth_; + for (auto const& child_pv : pv.lv->children) + { + // Note: place_pv incorporates child's transform + this->place_pv(transform, child_pv, proto); + } + --depth_; + } + else + { + // LV is referenced more than once *AND* has children + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " -> " + << "new universe at " << StreamableVariant{pv.transform} + << std::endl; + } + + auto [iter, inserted] = protos_.insert({pv.lv.get(), nullptr}); + if (inserted) + { + ++depth_; + // Construct pv proto + iter->second = (*this)(*pv.lv); + --depth_; + } + CELER_ASSERT(iter->second); + proto->daughters.push_back({iter->second, transform, ZOrder::media}); + + if (CELER_UNLIKELY(verbose_)) + { + std::clog << std::string(depth_, ' ') << " : " + << "daughter shape is " + << to_string(*proto->daughters.back().make_interior()); + } + } +} + +//---------------------------------------------------------------------------// +} // namespace g4org +} // namespace celeritas diff --git a/src/orange/g4org/ProtoConstructor.hh b/src/orange/g4org/ProtoConstructor.hh new file mode 100644 index 0000000000..13c590a7ed --- /dev/null +++ b/src/orange/g4org/ProtoConstructor.hh @@ -0,0 +1,68 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/g4org/ProtoConstructor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "orange/orangeinp/ObjectInterface.hh" +#include "orange/orangeinp/UnitProto.hh" + +#include "Volume.hh" + +namespace celeritas +{ +namespace g4org +{ +//---------------------------------------------------------------------------// +/*! + * Recursively build ORANGE proto-universes from a \c LogicalVolume . + * + * The input to this function is the output of \c LogicalVolumeConverter . This + * class is responsible for "placing" the converted \c PhysicalVolume by + * transforming its children. Depending on heuristics, the children are + * directly inserted into a \c UnitProto as volumes (specifically, the logical + * volume becomes a \c UnitProto::MaterialInput), or a \c LogicalVolume is + * turned into a \em new \c UnitProto that can be used in multiple locations. + */ +class ProtoConstructor +{ + public: + //!@{ + //! \name Type aliases + using SPConstObject = std::shared_ptr; + using ObjLv = std::pair; + using SPUnitProto = std::shared_ptr; + using ProtoInput = orangeinp::UnitProto::Input; + //!@} + + public: + //! Construct with verbosity setting + explicit ProtoConstructor(bool verbose) : verbose_{verbose} {} + + // Construct a proto from a logical volume + SPUnitProto operator()(LogicalVolume const& pv); + + private: + std::unordered_map protos_; + int depth_{0}; + bool verbose_{false}; + + // Place a physical volume into the given unconstructed proto + void place_pv(VariantTransform const& parent_transform, + PhysicalVolume const& pv, + ProtoInput* proto); + + // (TODO: make this configurable) + //! Number of daughters above which we use a "fill" material + static constexpr int fill_daughter_threshold() { return 2; } +}; + +//---------------------------------------------------------------------------// +} // namespace g4org +} // namespace celeritas diff --git a/src/orange/orangeinp/ProtoInterface.cc b/src/orange/orangeinp/ProtoInterface.cc index 7bb0d1d1a7..dff993c767 100644 --- a/src/orange/orangeinp/ProtoInterface.cc +++ b/src/orange/orangeinp/ProtoInterface.cc @@ -7,6 +7,10 @@ //---------------------------------------------------------------------------// #include "ProtoInterface.hh" +#include "corecel/io/ScopedTimeLog.hh" +#include "corecel/sys/ScopedMem.hh" +#include "corecel/sys/ScopedProfiling.hh" + #include "detail/InputBuilder.hh" namespace celeritas @@ -19,6 +23,10 @@ namespace orangeinp */ OrangeInput build_input(Tolerance<> const& tol, ProtoInterface const& global) { + ScopedProfiling profile_this{"build-orange-geo"}; + ScopedMem record_mem("orangeinp::build_input"); + ScopedTimeLog scoped_time; + OrangeInput result; detail::ProtoMap const protos{global}; CELER_ASSERT(protos.find(&global) == orange_global_universe); diff --git a/src/orange/orangeinp/UnitProto.cc b/src/orange/orangeinp/UnitProto.cc index 93e4e70ed0..6d78cc9bdc 100644 --- a/src/orange/orangeinp/UnitProto.cc +++ b/src/orange/orangeinp/UnitProto.cc @@ -104,7 +104,6 @@ void UnitProto::build(InputBuilder& input) const // Get the list of all surfaces actually used auto const sorted_local_surfaces = calc_surfaces(csg_unit.tree); - bool const has_background = csg_unit.background != MaterialId{}; UnitInput result; result.label = input_.label; @@ -156,7 +155,8 @@ void UnitProto::build(InputBuilder& input) const detail::PostfixLogicBuilder build_logic{csg_unit.tree, sorted_local_surfaces}; detail::InternalSurfaceFlagger has_internal_surfaces{csg_unit.tree}; - result.volumes.reserve(csg_unit.volumes.size() + has_background); + result.volumes.reserve(csg_unit.volumes.size() + + static_cast(csg_unit.background)); for (auto vol_idx : range(csg_unit.volumes.size())) { @@ -185,7 +185,7 @@ void UnitProto::build(InputBuilder& input) const result.volumes.emplace_back(std::move(vi)); } - if (has_background) + if (csg_unit.background) { // "Background" should be unreachable: 'nowhere' logic, null bbox // but it has to have all the surfaces that connect to an interior @@ -196,11 +196,14 @@ void UnitProto::build(InputBuilder& input) const vi.logic = {logic::ltrue, logic::lnot}; vi.bbox = {}; // XXX: input converter changes to infinite bbox vi.zorder = ZOrder::background; - vi.flags = VolumeRecord::implicit_vol; + // XXX the nearest internal surface is probably *not* the safety + // distance, but it's better than nothing + vi.flags = VolumeRecord::implicit_vol | VolumeRecord::simple_safety; result.volumes.emplace_back(std::move(vi)); } CELER_ASSERT(result.volumes.size() - == csg_unit.volumes.size() + has_background); + == csg_unit.volumes.size() + + static_cast(csg_unit.background)); // Set labels and other attributes. // NOTE: this means we're entirely ignoring the "metadata" from the CSG @@ -253,14 +256,18 @@ void UnitProto::build(InputBuilder& input) const // Save attributes from materials for (auto const& m : input_.materials) { - vol_iter->label = std::string{m.interior->label()}; + vol_iter->label = !m.label.empty() + ? m.label + : Label{std::string(m.interior->label())}; vol_iter->zorder = ZOrder::media; ++vol_iter; } - if (input_.fill) + if (input_.background) { - vol_iter->label = {input_.label, "bg"}; + vol_iter->label = !input_.background.label.empty() + ? input_.background.label + : Label{input_.label, "bg"}; ++vol_iter; } CELER_EXPECT(vol_iter == result.volumes.end()); @@ -294,7 +301,7 @@ auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit }; // Build exterior volume and optional background fill - if (input_.boundary.zorder != ZOrder::media && !input_.fill) + if (input_.boundary.zorder != ZOrder::media && !input_.background) { CELER_NOT_IMPLEMENTED("implicit exterior without background fill"); } @@ -318,11 +325,14 @@ auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit for (auto const& m : input_.materials) { auto lv = build_volume(*m.interior); - unit_builder.fill_volume(lv, m.fill); + if (m.fill) + { + unit_builder.fill_volume(lv, m.fill); + } } // Build background fill (optional) - result.background = input_.fill; + result.background = input_.background.fill; if (ext == ExteriorBoundary::is_daughter) { diff --git a/src/orange/orangeinp/UnitProto.hh b/src/orange/orangeinp/UnitProto.hh index 76360d2b28..188ed5c996 100644 --- a/src/orange/orangeinp/UnitProto.hh +++ b/src/orange/orangeinp/UnitProto.hh @@ -77,11 +77,22 @@ class UnitProto : public ProtoInterface using Unit = detail::CsgUnit; using Tol = Tolerance<>; + //! Optional "background" inside of exterior, outside of all mat/daughter + struct BackgroundInput + { + MaterialId fill{}; + Label label; + + // True if fill or label is specified + explicit inline operator bool() const; + }; + //! A homogeneous physical material struct MaterialInput { SPConstObject interior; MaterialId fill; + Label label; // True if fully defined explicit inline operator bool() const; @@ -114,9 +125,9 @@ class UnitProto : public ProtoInterface //! Required input data to create a unit proto struct Input { + BackgroundInput background; std::vector materials; std::vector daughters; - MaterialId fill{}; //!< Optional "background" material BoundaryInput boundary; std::string label; @@ -157,13 +168,22 @@ class UnitProto : public ProtoInterface Input input_; }; +//---------------------------------------------------------------------------// +/*! + * True if either material or label is provided. + */ +UnitProto::BackgroundInput::operator bool() const +{ + return this->fill || !this->label.empty(); +} + //---------------------------------------------------------------------------// /*! * True if fully defined. */ UnitProto::MaterialInput::operator bool() const { - return this->interior && this->fill; + return static_cast(this->interior); } //---------------------------------------------------------------------------// @@ -193,7 +213,8 @@ UnitProto::BoundaryInput::operator bool() const */ UnitProto::Input::operator bool() const { - return (!this->materials.empty() || !this->daughters.empty() || this->fill) + return (!this->materials.empty() || !this->daughters.empty() + || this->background) && this->boundary; } diff --git a/test/Test.cc b/test/Test.cc index 0c34e5840c..b9e0be6380 100644 --- a/test/Test.cc +++ b/test/Test.cc @@ -46,10 +46,10 @@ Test::test_data_path(std::string_view subdir, std::string_view filename) /*! * Replace pointer addresses with 0x0 for improved testability. */ -[[nodiscard]] std::string Test::genericize_pointers(std::string const& s) +[[nodiscard]] std::string Test::genericize_pointers(std::string_view s) { static std::regex const subs_ptr("0x[0-9a-f]{2,}"); - return std::regex_replace(s, subs_ptr, "0x0"); + return std::regex_replace(std::string{s}, subs_ptr, "0x0"); } //---------------------------------------------------------------------------// diff --git a/test/Test.hh b/test/Test.hh index 9b7999c2d0..8da88f5469 100644 --- a/test/Test.hh +++ b/test/Test.hh @@ -45,8 +45,8 @@ class Test : public ::testing::Test static std::string test_data_path(std::string_view subdir, std::string_view filename); - // Replace pointer addresses with 0x0 for improved testability. - [[nodiscard]] static std::string genericize_pointers(std::string const& s); + // Replace pointer addresses with 0x0 for improved testability + [[nodiscard]] static std::string genericize_pointers(std::string_view s); // True if CELER_TEST_STRICT is set (under CI) static bool strict_testing(); diff --git a/test/geocel/data/testem3.gdml b/test/geocel/data/testem3.gdml index 7d0462a3f8..f58e1b39c9 100644 --- a/test/geocel/data/testem3.gdml +++ b/test/geocel/data/testem3.gdml @@ -76,7 +76,7 @@ - + @@ -105,7 +105,7 @@ - + diff --git a/test/orange/CMakeLists.txt b/test/orange/CMakeLists.txt index 0f71e0f79d..6d39a02cab 100644 --- a/test/orange/CMakeLists.txt +++ b/test/orange/CMakeLists.txt @@ -129,11 +129,13 @@ celeritas_add_test(univ/detail/SenseCalculator.test.cc) # Geant4 construction if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") set(CELERITASTEST_PREFIX orange/g4org) - list(APPEND CELERITASTEST_LINK_LIBRARIES ${_g4_geo_libs}) - celeritas_add_test(g4org/SolidConverter.test.cc) + celeritas_add_test(g4org/SolidConverter.test.cc + LINK_LIBRARIES ${_g4_geo_libs}) celeritas_add_test(g4org/PhysicalVolumeConverter.test.cc) - celeritas_add_test(g4org/Transformer.test.cc) + celeritas_add_test(g4org/ProtoConstructor.test.cc) + celeritas_add_test(g4org/Transformer.test.cc + LINK_LIBRARIES ${_g4_geo_libs}) endif() #-----------------------------------------------------------------------------# diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index caf4ffd388..b1c5354787 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -1356,8 +1356,6 @@ TEST_F(InputBuilderTest, hierarchy) EXPECT_VEC_EQ(expected_volumes, result.volumes); static real_type const expected_distances[] = {14, 2, 8, 2, 94}; EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {7, 0, 4, 0, 19}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); } { SCOPED_TRACE("py_filled"); @@ -1372,7 +1370,7 @@ TEST_F(InputBuilderTest, hierarchy) static real_type const expected_distances[] = {3, 2, 8, 2, 4, 87.979589711327}; EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {0, 0, 0, 0, 0, 39}; + static real_type const expected_hw_safety[] = {1.5, 5, 4, 5, 2, 39}; EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); } { diff --git a/test/orange/data/inputbuilder-bgspheres.org.json b/test/orange/data/inputbuilder-bgspheres.org.json index d71f05ab21..fc6225370d 100644 --- a/test/orange/data/inputbuilder-bgspheres.org.json +++ b/test/orange/data/inputbuilder-bgspheres.org.json @@ -108,7 +108,7 @@ 1, 2 ], -"flags": 2, +"flags": 6, "logic": "* ~", "zorder": "B" } diff --git a/test/orange/data/inputbuilder-hierarchy.org.json b/test/orange/data/inputbuilder-hierarchy.org.json index 2936ba036e..9be762087a 100644 --- a/test/orange/data/inputbuilder-hierarchy.org.json +++ b/test/orange/data/inputbuilder-hierarchy.org.json @@ -293,7 +293,7 @@ { "bbox": null, "faces": [], -"flags": 2, +"flags": 6, "logic": "* ~", "zorder": "B" } @@ -336,7 +336,7 @@ { "bbox": null, "faces": [], -"flags": 2, +"flags": 6, "logic": "* ~", "zorder": "B" } @@ -521,7 +521,7 @@ 2, 3 ], -"flags": 2, +"flags": 6, "logic": "* ~", "zorder": "B" } @@ -645,7 +645,7 @@ { "bbox": null, "faces": [], -"flags": 2, +"flags": 6, "logic": "* ~", "zorder": "B" } @@ -688,7 +688,7 @@ { "bbox": null, "faces": [], -"flags": 2, +"flags": 6, "logic": "* ~", "zorder": "B" } diff --git a/test/orange/g4org/ProtoConstructor.test.cc b/test/orange/g4org/ProtoConstructor.test.cc new file mode 100644 index 0000000000..90313115d8 --- /dev/null +++ b/test/orange/g4org/ProtoConstructor.test.cc @@ -0,0 +1,472 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/g4org/ProtoConstructor.test.cc +//---------------------------------------------------------------------------// +#include "orange/g4org/ProtoConstructor.hh" + +#include "corecel/io/Repr.hh" +#include "geocel/GeantGeoUtils.hh" +#include "orange/g4org/PhysicalVolumeConverter.hh" +#include "orange/orangeinp/CsgTestUtils.hh" +#include "orange/orangeinp/detail/CsgUnit.hh" +#include "orange/orangeinp/detail/ProtoMap.hh" + +#include "celeritas_test.hh" + +using namespace celeritas::orangeinp::test; +using celeritas::orangeinp::UnitProto; +using celeritas::orangeinp::detail::ProtoMap; +using ExteriorBoundary = celeritas::orangeinp::UnitProto::ExteriorBoundary; + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace g4org +{ +namespace test +{ +//---------------------------------------------------------------------------// +class ProtoConstructorTest : public ::celeritas::test::Test +{ + protected: + using Unit = orangeinp::detail::CsgUnit; + using Tol = Tolerance<>; + + LogicalVolume load_impl(std::string const& path) + { + G4VPhysicalVolume const* g4world + = ::celeritas::load_geant_geometry_native(path); + CELER_ASSERT(g4world); + PhysicalVolumeConverter::Options opts; + opts.verbose = false; + opts.scale = 0.1; + PhysicalVolumeConverter convert{std::move(opts)}; + PhysicalVolume world = convert(*g4world); + + EXPECT_TRUE(std::holds_alternative(world.transform)); + EXPECT_EQ(1, world.lv.use_count()); + return std::move(*world.lv); + } + + LogicalVolume load(std::string const& filename) + { + return this->load_impl(this->test_data_path("geocel", filename)); + } + + auto get_proto_names(ProtoMap const& protos) const + { + std::vector result; + for (auto uid : range(UniverseId{protos.size()})) + { + result.push_back( + this->genericize_pointers(protos.at(uid)->label())); + } + return result; + } + + auto build_unit(ProtoMap const& protos, UniverseId id) const + { + CELER_EXPECT(id); + auto const* proto = dynamic_cast(protos.at(id)); + CELER_ASSERT(proto); + return proto->build(tol_, + id == UniverseId{0} + ? ExteriorBoundary::is_global + : ExteriorBoundary::is_daughter); + } + + void TearDown() final { ::celeritas::reset_geant_geometry(); } + + Tolerance<> tol_ = Tol::from_relative(1e-5); +}; + +//---------------------------------------------------------------------------// +TEST_F(ProtoConstructorTest, two_boxes) +{ + LogicalVolume world = this->load("two-boxes.gdml"); + auto global_proto = ProtoConstructor(/* verbose = */ true)(world); + ProtoMap protos{*global_proto}; + ASSERT_EQ(1, protos.size()); + { + SCOPED_TRACE("global"); + auto u = this->build_unit(protos, UniverseId{0}); + + static char const* const expected_surface_strings[] = { + "Plane: x=-500", + "Plane: x=500", + "Plane: y=-500", + "Plane: y=500", + "Plane: z=-500", + "Plane: z=500", + "Plane: x=-5", + "Plane: x=5", + "Plane: y=-5", + "Plane: y=5", + "Plane: z=-5", + "Plane: z=5", + }; + static char const* const expected_volume_strings[] = { + "!all(+0, -1, +2, -3, +4, -5)", + "all(+6, -7, +8, -9, +10, -11)", + "all(all(+0, -1, +2, -3, +4, -5), !all(+6, -7, +8, -9, +10, -11))", + }; + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + } +} + +//---------------------------------------------------------------------------// +TEST_F(ProtoConstructorTest, intersection_boxes) +{ + LogicalVolume world = this->load("intersection-boxes.gdml"); + auto global_proto = ProtoConstructor(/* verbose = */ false)(world); + ProtoMap protos{*global_proto}; + ASSERT_EQ(1, protos.size()); + { + SCOPED_TRACE("global"); + auto u = this->build_unit(protos, UniverseId{0}); + + static char const* const expected_surface_strings[] = { + "Plane: x=-50", + "Plane: x=50", + "Plane: y=-50", + "Plane: y=50", + "Plane: z=-50", + "Plane: z=50", + "Plane: x=-1", + "Plane: x=1", + "Plane: y=-1.5", + "Plane: y=1.5", + "Plane: z=-2", + "Plane: z=2", + "Plane: n={0.86603,0,-0.5}, d=-2.634", + "Plane: n={0.86603,0,-0.5}, d=0.36603", + "Plane: y=0", + "Plane: y=4", + "Plane: n={0.5,0,0.86603}, d=1.4641", + "Plane: n={0.5,0,0.86603}, d=6.4641", + }; + static char const* const expected_volume_strings[] = { + "!all(+0, -1, +2, -3, +4, -5)", + "all(all(+6, -7, +8, -9, +10, -11), all(+12, -13, +14, -15, +16, " + "-17))", + "all(all(+0, -1, +2, -3, +4, -5), !all(all(+6, -7, +8, -9, +10, " + "-11), all(+12, -13, +14, -15, +16, -17)))", + }; + static char const* const expected_md_strings[] = { + "", + "", + "world_box@mx", + "world_box@px", + "", + "world_box@my", + "world_box@py", + "", + "world_box@mz", + "world_box@pz", + "", + "world_box", + "[EXTERIOR]", + "first@mx", + "first@px", + "", + "first@my", + "first@py", + "", + "first@mz", + "first@pz", + "", + "first", + "second@mx", + "second@px", + "", + "second@my", + "second@py", + "", + "second@mz", + "second@pz", + "", + "second", + "isect", + "", + "world0x0", + }; + static char const* const expected_bound_strings[] = { + "11: {{{-50,-50,-50}, {50,50,50}}, {{-50,-50,-50}, {50,50,50}}}", + "~12: {{{-50,-50,-50}, {50,50,50}}, {{-50,-50,-50}, {50,50,50}}}", + "22: {{{-1,-1.5,-2}, {1,1.5,2}}, {{-1,-1.5,-2}, {1,1.5,2}}}", + "32: {null, {{-1.55,0,1.08}, {3.55,4,6.92}}}", + "33: {null, {{-1,0,1.08}, {1,1.5,2}}}", + "~34: {null, {{-1,0,1.08}, {1,1.5,2}}}", + "35: {{{-1,0,1.08}, {1,1.5,2}}, {{-50,-50,-50}, {50,50,50}}}", + }; + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); + EXPECT_VEC_EQ(expected_bound_strings, bound_strings(u)); + } +} + +//---------------------------------------------------------------------------// +TEST_F(ProtoConstructorTest, simple_cms) +{ + // NOTE: GDML stores widths for box and cylinder Z; Geant4 uses halfwidths + LogicalVolume world = this->load("simple-cms.gdml"); + + auto global_proto = ProtoConstructor(/* verbose = */ true)(world); + ProtoMap protos{*global_proto}; + + static std::string const expected_proto_names[] = {"world0x0"}; + EXPECT_VEC_EQ(expected_proto_names, get_proto_names(protos)); + + ASSERT_EQ(1, protos.size()); + { + SCOPED_TRACE("global"); + auto u = this->build_unit(protos, UniverseId{0}); + + static char const* const expected_surface_strings[] = { + "Plane: x=-1000", + "Plane: x=1000", + "Plane: y=-1000", + "Plane: y=1000", + "Plane: z=-2000", + "Plane: z=2000", + "Plane: z=-700", + "Plane: z=700", + "Cyl z: r=30", + "Cyl z: r=125", + "Cyl z: r=175", + "Cyl z: r=275", + "Cyl z: r=375", + "Cyl z: r=700", + }; + static char const* const expected_volume_strings[] = { + "!all(+0, -1, +2, -3, +4, -5)", + "all(+6, -7, -8)", + "all(all(+6, -7, -9), !all(+6, -7, -8))", + "all(all(+6, -7, -10), !all(+6, -7, -9))", + "all(all(+6, -7, -11), !all(+6, -7, -10))", + "all(all(+6, -7, -12), !all(+6, -7, -11))", + "all(all(+6, -7, -13), !all(+6, -7, -12))", + }; + static char const* const expected_fill_strings[] + = {"", "m0", "m1", "m2", "m3", "m4", "m5"}; + static int const expected_volume_nodes[] = {12, 18, 23, 28, 33, 38, 43}; + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_fill_strings, fill_strings(u)); + EXPECT_VEC_EQ(expected_volume_nodes, volume_nodes(u)); + EXPECT_EQ(MaterialId{0}, u.background); + } +} + +//---------------------------------------------------------------------------// +TEST_F(ProtoConstructorTest, testem3) +{ + LogicalVolume world = this->load("testem3.gdml"); + + auto global_proto = ProtoConstructor(/* verbose = */ false)(world); + ProtoMap protos{*global_proto}; + + static std::string const expected_proto_names[] = {"World0x0", "Layer0x0"}; + EXPECT_VEC_EQ(expected_proto_names, get_proto_names(protos)); + + ASSERT_EQ(2, protos.size()); + { + SCOPED_TRACE("global"); + auto u = this->build_unit(protos, UniverseId{0}); + + // NOTE: 51 layer X surfaces, 4 surrounding, 6 world, plus whatever + // "unused" surfaces from deduplication + auto surfaces = surface_strings(u); + EXPECT_LE(51 + 4 + 6, surfaces.size()) << repr(surfaces); + + auto transforms = transform_strings(u); + EXPECT_EQ(58, transforms.size()) << repr(transforms); + EXPECT_EQ("28: t=3 -> {{-18,0,0}}", transforms[4]); + + auto bounds = bound_strings(u); + ASSERT_EQ(transforms.size(), bounds.size()); + EXPECT_EQ( + "28: {{{-18.4,-20,-20}, {-17.6,20,20}}, {{-18.4,-20,-20}, " + "{-17.6,20,20}}}", + bounds[4]); + + auto vols = volume_strings(u); + ASSERT_EQ(53, vols.size()); // slabs, zero-size 'calo', world, + // exterior + EXPECT_EQ( + "all(all(+0, -1, +2, -3, +4, -5), !all(+6, +8, -9, +10, -11, " + "-84))", + vols.back()); + EXPECT_EQ(MaterialId{}, u.background); + } + { + SCOPED_TRACE("daughter"); + auto u = this->build_unit(protos, UniverseId{1}); + + static char const* const expected_surface_strings[] = { + "Plane: x=-0.4", + "Plane: x=0.4", + "Plane: y=-20", + "Plane: y=20", + "Plane: z=-20", + "Plane: z=20", + "Plane: x=-0.17", + }; + static char const* const expected_volume_strings[] + = {"F", "-6", "+6", "!any(+6, -6)"}; + static char const* const expected_md_strings[] = { + "", + "", + "Absorber1@mx,Layer@mx", + "Absorber2@px,Layer@px", + "", + "Absorber1@my,Absorber2@my,Layer@my", + "Absorber1@py,Absorber2@py,Layer@py", + "", + "Absorber1@mz,Absorber2@mz,Layer@mz", + "Absorber1@pz,Absorber2@pz,Layer@pz", + "", + "Layer", + "[EXTERIOR]", + "Absorber1@px,Absorber2@mx", + "", + "Absorber1", + "Absorber2", + "Layer0x0.children", + "", + "Layer0x0", + }; + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); + } +} + +//---------------------------------------------------------------------------// +TEST_F(ProtoConstructorTest, znenv) +{ + LogicalVolume world = this->load("znenv.gdml"); + + auto global_proto = ProtoConstructor(/* verbose = */ false)(world); + ProtoMap protos{*global_proto}; + + static std::string const expected_proto_names[] + = {"World0x0", "ZNTX0x0", "ZN10x0", "ZNSL0x0", "ZNST0x0"}; + EXPECT_VEC_EQ(expected_proto_names, get_proto_names(protos)); + + ASSERT_EQ(5, protos.size()); + { + SCOPED_TRACE("World"); + auto u = this->build_unit(protos, UniverseId{0}); + + // clang-format off + static char const* const expected_surface_strings[] = { + "Plane: x=-50", "Plane: x=50", "Plane: y=-50", + "Plane: y=50", "Plane: z=-100", "Plane: z=100", + "Plane: x=-3.52", "Plane: x=0", "Plane: y=-3.52", + "Plane: y=3.52", "Plane: z=-50", "Plane: z=50", + "Plane: x=3.52", "Plane: x=-3.62", "Plane: x=3.62", + "Plane: y=-3.62", "Plane: y=3.62", "Plane: z=-50.1", + "Plane: z=50.1", + }; + static char const* const expected_fill_strings[] = { + "", + "{u=0, t=1}", + "{u=1, t=2}", + "m3", + "m2", + "m3", + }; + static int const expected_volume_nodes[] = {12, 22, 25, 38, 41, 43}; + static char const expected_tree_string[] + = R"json(["t",["~",0],["S",0],["S",1],["~",3],["S",2],["S",3],["~",6],["S",4],["S",5],["~",9],["&",[2,4,5,7,8,10]],["~",11],["S",6],["S",7],["~",14],["S",8],["S",9],["~",17],["S",10],["S",11],["~",20],["&",[13,15,16,18,19,21]],["S",12],["~",23],["&",[14,16,18,19,21,24]],["S",13],["S",14],["~",27],["S",15],["S",16],["~",30],["S",17],["S",18],["~",33],["&",[26,28,29,31,32,34]],["&",[13,16,18,19,21,24]],["~",36],["&",[35,37]],["|",[22,25]],["~",39],["&",[36,40]],["~",35],["&",[11,42]]])json"; + // clang-format on + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_fill_strings, fill_strings(u)); + EXPECT_VEC_EQ(expected_volume_nodes, volume_nodes(u)); + if (CELERITAS_USE_JSON) + { + EXPECT_JSON_EQ(expected_tree_string, tree_string(u)); + } + EXPECT_EQ(MaterialId{}, u.background); + } + { + SCOPED_TRACE("ZNTX"); + auto u = this->build_unit(protos, UniverseId{1}); + + static char const* const expected_surface_strings[] = { + "Plane: x=-1.76", + "Plane: x=1.76", + "Plane: y=-3.52", + "Plane: y=3.52", + "Plane: z=-50", + "Plane: z=50", + "Plane: y=0", + }; + static char const* const expected_volume_strings[] + = {"F", "-6", "+6", "!any(+6, -6)"}; + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_EQ(MaterialId{}, u.background); + } + { + SCOPED_TRACE("ZNST"); + auto u = this->build_unit(protos, UniverseId{4}); + + static char const* const expected_surface_strings[] = { + "Plane: x=-0.16", + "Plane: x=0.16", + "Plane: y=-0.16", + "Plane: y=0.16", + "Plane: z=-50", + "Plane: z=50", + "Plane: x=-0.11", + "Plane: x=-0.05", + "Plane: y=0.05", + "Plane: y=0.11", + "Cyl z: r=0.01825 at x=-0.16, y=0.16", + "Cyl z: r=0.01825 at x=-0.08, y=0.08", + "Plane: x=0.05", + "Plane: x=0.11", + "Cyl z: r=0.01825 at x=0.16, y=0.16", + "Cyl z: r=0.01825 at x=0.08, y=0.08", + "Plane: y=-0.11", + "Plane: y=-0.05", + "Cyl z: r=0.01825 at x=-0.16, y=-0.16", + "Cyl z: r=0.01825 at x=-0.08, y=-0.08", + "Cyl z: r=0.01825 at x=0.16, y=-0.16", + "Cyl z: r=0.01825 at x=0.08, y=-0.08", + }; + static char const* const expected_volume_strings[] = { + "F", + "all(all(+6, -7, +8, -9), +10)", + "-11", + "all(all(+8, -9, +12, -13), +14)", + "-15", + "all(all(+6, -7, +16, -17), +18)", + "-19", + "all(all(+12, -13, +16, -17), +20)", + "-21", + }; + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_EQ(MaterialId{2}, u.background); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace g4org +} // namespace celeritas diff --git a/test/orange/orangeinp/CsgTestUtils.cc b/test/orange/orangeinp/CsgTestUtils.cc index 49051fe784..811f21263c 100644 --- a/test/orange/orangeinp/CsgTestUtils.cc +++ b/test/orange/orangeinp/CsgTestUtils.cc @@ -25,6 +25,8 @@ #include "orange/surf/SurfaceIO.hh" #include "orange/transform/TransformIO.hh" +#include "Test.hh" + #if CELERITAS_USE_JSON # include @@ -111,7 +113,17 @@ std::vector md_strings(CsgUnit const& u) std::vector result; for (auto const& md_set : u.metadata) { - result.push_back(to_string(join(md_set.begin(), md_set.end(), ','))); + result.push_back(to_string(join_stream( + md_set.begin(), + md_set.end(), + ',', + [](std::ostream& os, Label const& l) { + os << ::celeritas::test::Test::genericize_pointers(l.name); + if (!l.ext.empty()) + { + os << Label::default_sep << l.ext; + } + }))); } return result; } diff --git a/test/orange/orangeinp/UnitProto.test.cc b/test/orange/orangeinp/UnitProto.test.cc index 184ff2418f..43076ce361 100644 --- a/test/orange/orangeinp/UnitProto.test.cc +++ b/test/orange/orangeinp/UnitProto.test.cc @@ -83,7 +83,7 @@ SPConstProto make_daughter(std::string label) { UnitProto::Input inp; inp.boundary.interior = make_sph(label + ":ext", 1); - inp.fill = MaterialId{0}; + inp.background.fill = MaterialId{0}; inp.label = std::move(label); return std::make_shared(std::move(inp)); @@ -104,6 +104,16 @@ std::string proto_labels(ProtoInterface::VecProto const& vp) return to_string(join_stream(vp.begin(), vp.end(), ",", stream_proto_ptr)); } +UnitProto::MaterialInput +make_material(SPConstObject&& obj, MaterialId::size_type m) +{ + CELER_EXPECT(obj); + UnitProto::MaterialInput result; + result.interior = std::move(obj); + result.fill = MaterialId{m}; + return result; +} + //---------------------------------------------------------------------------// class UnitProtoTest : public ::celeritas::test::Test { @@ -124,11 +134,10 @@ TEST_F(LeafTest, explicit_exterior) inp.boundary.interior = make_cyl("bound", 1.0, 1.0); inp.boundary.zorder = ZOrder::media; inp.label = "leaf"; - inp.materials.push_back( - {make_translated(make_cyl("bottom", 1, 0.5), {0, 0, -0.5}), - MaterialId{1}}); - inp.materials.push_back( - {make_translated(make_cyl("top", 1, 0.5), {0, 0, 0.5}), MaterialId{2}}); + inp.materials.push_back(make_material( + make_translated(make_cyl("bottom", 1, 0.5), {0, 0, -0.5}), 1)); + inp.materials.push_back(make_material( + make_translated(make_cyl("top", 1, 0.5), {0, 0, 0.5}), 2)); UnitProto const proto{std::move(inp)}; EXPECT_EQ("", proto_labels(proto.daughters())); @@ -178,9 +187,9 @@ TEST_F(LeafTest, implicit_exterior) UnitProto::Input inp; inp.boundary.interior = make_cyl("bound", 1.0, 1.0); inp.boundary.zorder = ZOrder::exterior; - inp.fill = MaterialId{0}; + inp.background.fill = MaterialId{0}; inp.label = "leaf"; - inp.materials.push_back({make_cyl("middle", 1, 0.5), MaterialId{1}}); + inp.materials.push_back(make_material(make_cyl("middle", 1, 0.5), 1)); UnitProto const proto{std::move(inp)}; { @@ -223,9 +232,9 @@ TEST_F(MotherTest, explicit_exterior) inp.boundary.zorder = ZOrder::media; inp.label = "mother"; inp.materials.push_back( - {make_translated(make_sph("leaf", 1), {0, 0, -5}), MaterialId{1}}); + make_material(make_translated(make_sph("leaf", 1), {0, 0, -5}), 1)); inp.materials.push_back( - {make_translated(make_sph("leaf2", 1), {0, 0, 5}), MaterialId{2}}); + make_material(make_translated(make_sph("leaf2", 1), {0, 0, 5}), 2)); inp.daughters.push_back({make_daughter("d1"), Translation{{0, 5, 0}}}); inp.daughters.push_back( {make_daughter("d2"), @@ -243,7 +252,7 @@ TEST_F(MotherTest, explicit_exterior) interior.push_back({Sense::outside, d.make_interior()}); } inp.materials.push_back( - {make_rdv("interior", std::move(interior)), MaterialId{3}}); + make_material(make_rdv("interior", std::move(interior)), 3)); UnitProto const proto{std::move(inp)}; @@ -326,14 +335,14 @@ TEST_F(MotherTest, implicit_exterior) inp.boundary.zorder = ZOrder::media; inp.label = "mother"; inp.materials.push_back( - {make_translated(make_sph("leaf", 1), {0, 0, -5}), MaterialId{1}}); + make_material(make_translated(make_sph("leaf", 1), {0, 0, -5}), 1)); inp.materials.push_back( - {make_translated(make_sph("leaf2", 1), {0, 0, 5}), MaterialId{2}}); + make_material(make_translated(make_sph("leaf2", 1), {0, 0, 5}), 2)); inp.daughters.push_back({make_daughter("d1"), Translation{{0, 5, 0}}}); inp.daughters.push_back( {make_daughter("d2"), Transformation{make_rotation(Axis::x, Turn{0.25}), {0, -5, 0}}}); - inp.fill = MaterialId{3}; + inp.background.fill = MaterialId{3}; UnitProto const proto{std::move(inp)}; @@ -364,11 +373,11 @@ TEST_F(MotherTest, fuzziness) inp.boundary.zorder = ZOrder::media; inp.label = "fuzzy"; inp.daughters.push_back({make_daughter("d1"), {}}); - inp.materials.push_back( - {make_rdv("interior", - {{Sense::inside, inp.boundary.interior}, - {Sense::outside, make_sph("similar", 1.0001)}}), - MaterialId{1}}); + inp.materials.push_back(make_material( + make_rdv("interior", + {{Sense::inside, inp.boundary.interior}, + {Sense::outside, make_sph("similar", 1.0001)}}), + 1)); UnitProto const proto{std::move(inp)}; @@ -457,11 +466,11 @@ TEST_F(InputBuilderTest, globalspheres) // Construct "inside" cell inp.materials.push_back( - {make_rdv("shell", - {{Sense::inside, inp.boundary.interior}, - {Sense::outside, inner}}), - MaterialId{1}}); - inp.materials.push_back({inner, MaterialId{2}}); + make_material(make_rdv("shell", + {{Sense::inside, inp.boundary.interior}, + {Sense::outside, inner}}), + 1)); + inp.materials.push_back(make_material(std::move(inner), 2)); return inp; }()}; @@ -475,12 +484,11 @@ TEST_F(InputBuilderTest, bgspheres) inp.boundary.interior = make_sph("bound", 10.0); inp.label = "global"; - inp.materials.push_back( - {make_translated(make_sph("top", 2.0), {0, 0, 3}), MaterialId{1}}); - inp.materials.push_back( - {make_translated(make_sph("bottom", 3.0), {0, 0, -3}), - MaterialId{2}}); - inp.fill = MaterialId{3}; + inp.materials.push_back(make_material( + make_translated(make_sph("top", 2.0), {0, 0, 3}), 1)); + inp.materials.push_back(make_material( + make_translated(make_sph("bottom", 3.0), {0, 0, -3}), 2)); + inp.background.fill = MaterialId{3}; return inp; }()}; @@ -498,7 +506,7 @@ TEST_F(InputBuilderTest, universes) inp.boundary.interior = patricia; inp.boundary.zorder = ZOrder::media; inp.materials.push_back( - {make_rdv("patty", {{Sense::inside, patricia}}), MaterialId{2}}); + make_material(make_rdv("patty", {{Sense::inside, patricia}}), 2)); return inp; }()); @@ -513,16 +521,16 @@ TEST_F(InputBuilderTest, universes) inp.boundary.zorder = ZOrder::media; inp.daughters.push_back({most_inner, Translation{{-2, -2, 0}}}); inp.materials.push_back( - {make_rdv("a", {{Sense::inside, alpha}}), MaterialId{0}}); + make_material(make_rdv("a", {{Sense::inside, alpha}}), 0)); inp.materials.push_back( - {make_rdv("b", {{Sense::inside, beta}}), MaterialId{1}}); - inp.materials.push_back( - {make_rdv("c", - {{Sense::outside, alpha}, - {Sense::outside, beta}, - {Sense::inside, gamma}, - {Sense::outside, inp.daughters[0].make_interior()}}), - MaterialId{2}}); + make_material(make_rdv("b", {{Sense::inside, beta}}), 1)); + inp.materials.push_back(make_material( + make_rdv("c", + {{Sense::outside, alpha}, + {Sense::outside, beta}, + {Sense::inside, gamma}, + {Sense::outside, inp.daughters[0].make_interior()}}), + 2)); return inp; }()); @@ -539,14 +547,14 @@ TEST_F(InputBuilderTest, universes) inp.daughters.push_back( {inner, Translation{{2, -2, 0.5}}, ZOrder::media}); inp.materials.push_back( - {make_rdv("bobby", {{Sense::inside, bob}}), MaterialId{3}}); - inp.materials.push_back( - {make_rdv("johnny", - {{Sense::outside, bob}, - {Sense::inside, john}, - {Sense::outside, inp.daughters[0].make_interior()}, - {Sense::outside, inp.daughters[1].make_interior()}}), - MaterialId{4}}); + make_material(make_rdv("bobby", {{Sense::inside, bob}}), 3)); + inp.materials.push_back(make_material( + make_rdv("johnny", + {{Sense::outside, bob}, + {Sense::inside, john}, + {Sense::outside, inp.daughters[0].make_interior()}, + {Sense::outside, inp.daughters[1].make_interior()}}), + 4)); return inp; }()); @@ -560,12 +568,10 @@ TEST_F(InputBuilderTest, hierarchy) inp.boundary.interior = make_cyl("bound", 1.0, 1.0); inp.boundary.zorder = ZOrder::media; inp.label = "leafy"; - inp.materials.push_back( - {make_translated(make_cyl("bottom", 1, 0.5), {0, 0, -0.5}), - MaterialId{1}}); - inp.materials.push_back( - {make_translated(make_cyl("top", 1, 0.5), {0, 0, 0.5}), - MaterialId{2}}); + inp.materials.push_back(make_material( + make_translated(make_cyl("bottom", 1, 0.5), {0, 0, -0.5}), 1)); + inp.materials.push_back(make_material( + make_translated(make_cyl("top", 1, 0.5), {0, 0, 0.5}), 2)); return inp; }()); @@ -574,15 +580,15 @@ TEST_F(InputBuilderTest, hierarchy) inp.boundary.interior = make_sph("bound", 10.0); inp.boundary.zorder = ZOrder::exterior; inp.label = "filled_daughter"; - inp.materials.push_back( - {make_translated(make_sph("leaf1", 1), {0, 0, -5}), MaterialId{1}}); - inp.materials.push_back( - {make_translated(make_sph("leaf2", 1), {0, 0, 5}), MaterialId{2}}); + inp.materials.push_back(make_material( + make_translated(make_sph("leaf1", 1), {0, 0, -5}), 1)); + inp.materials.push_back(make_material( + make_translated(make_sph("leaf2", 1), {0, 0, 5}), 2)); inp.daughters.push_back({make_daughter("d1"), Translation{{0, 5, 0}}}); inp.daughters.push_back( {make_daughter("d2"), Transformation{make_rotation(Axis::x, Turn{0.25}), {0, -5, 0}}}); - inp.fill = MaterialId{3}; + inp.background.fill = MaterialId{3}; return inp; }()); @@ -598,27 +604,27 @@ TEST_F(InputBuilderTest, hierarchy) inp.daughters.push_back({filled_daughter, Translation{{0, 0, -20}}}); inp.daughters.push_back({leaf, Translation{{0, 0, 20}}}); - inp.materials.push_back( - {make_translated(make_sph("leaf1", 1), {0, 0, -5}), MaterialId{1}}); + inp.materials.push_back(make_material( + make_translated(make_sph("leaf1", 1), {0, 0, -5}), 1)); // Construct "inside" cell - inp.materials.push_back( - {make_rdv("interior", - [&] { - VecSenseObj interior - = {{Sense::inside, inp.boundary.interior}}; - for (auto const& d : inp.daughters) - { - interior.push_back( - {Sense::outside, d.make_interior()}); - } - for (auto const& m : inp.materials) - { - interior.push_back({Sense::outside, m.interior}); - } - return interior; - }()), - MaterialId{3}}); + inp.materials.push_back(make_material( + make_rdv("interior", + [&] { + VecSenseObj interior + = {{Sense::inside, inp.boundary.interior}}; + for (auto const& d : inp.daughters) + { + interior.push_back( + {Sense::outside, d.make_interior()}); + } + for (auto const& m : inp.materials) + { + interior.push_back({Sense::outside, m.interior}); + } + return interior; + }()), + 3)); return inp; }()); From 55dbb28e1a383277bf368414fdfc0bea453caea9 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 16 Apr 2024 10:48:07 -0400 Subject: [PATCH 03/59] Add small utilities for rasterization work (#1185) * Generalize array "soft unit" and move to independent file * Add CELER_DEFAULT_MOVE_DELETE_COPY macro * Add CELER_JSON_PAIR helper --- src/celeritas/ext/GeantSetup.hh | 7 +- .../field/FieldDriverOptionsIO.json.cc | 28 ++-- src/celeritas/field/RZMapFieldInputIO.json.cc | 21 ++- src/corecel/Macros.hh | 12 ++ src/corecel/data/CollectionStateStore.hh | 9 +- src/corecel/io/JsonUtils.json.hh | 9 ++ src/corecel/io/detail/LoggerMessage.hh | 7 +- src/corecel/math/ArraySoftUnit.hh | 133 ++++++++++++++++++ src/corecel/math/ArrayUtils.hh | 44 +----- src/corecel/sys/ScopedMem.hh | 9 +- test/corecel/ScopedLogStorer.hh | 12 +- 11 files changed, 190 insertions(+), 101 deletions(-) create mode 100644 src/corecel/math/ArraySoftUnit.hh diff --git a/src/celeritas/ext/GeantSetup.hh b/src/celeritas/ext/GeantSetup.hh index 9ac1100013..4bd911673a 100644 --- a/src/celeritas/ext/GeantSetup.hh +++ b/src/celeritas/ext/GeantSetup.hh @@ -50,13 +50,8 @@ class GeantSetup // Terminate run on destruction ~GeantSetup(); - //!@{ //! Prevent copying but allow moving - GeantSetup(GeantSetup const&) = delete; - GeantSetup& operator=(GeantSetup const&) = delete; - GeantSetup(GeantSetup&&) = default; - GeantSetup& operator=(GeantSetup&&) = default; - //!@} + CELER_DEFAULT_MOVE_DELETE_COPY(GeantSetup); // Get the world detector volume inline G4VPhysicalVolume const* world() const; diff --git a/src/celeritas/field/FieldDriverOptionsIO.json.cc b/src/celeritas/field/FieldDriverOptionsIO.json.cc index 4fea06d956..9fca979be8 100644 --- a/src/celeritas/field/FieldDriverOptionsIO.json.cc +++ b/src/celeritas/field/FieldDriverOptionsIO.json.cc @@ -10,6 +10,8 @@ #include #include +#include "corecel/io/JsonUtils.json.hh" + #include "FieldDriverOptions.hh" namespace celeritas @@ -49,22 +51,20 @@ void from_json(nlohmann::json const& j, FieldDriverOptions& opts) */ void to_json(nlohmann::json& j, FieldDriverOptions const& opts) { -#define FDO_PAIR(FIELD) {#FIELD, opts.FIELD} j = nlohmann::json{ - FDO_PAIR(minimum_step), - FDO_PAIR(delta_chord), - FDO_PAIR(delta_intersection), - FDO_PAIR(epsilon_step), - FDO_PAIR(epsilon_rel_max), - FDO_PAIR(errcon), - FDO_PAIR(pgrow), - FDO_PAIR(pshrink), - FDO_PAIR(safety), - FDO_PAIR(max_stepping_increase), - FDO_PAIR(max_stepping_decrease), - FDO_PAIR(max_nsteps), + CELER_JSON_PAIR(opts, minimum_step), + CELER_JSON_PAIR(opts, delta_chord), + CELER_JSON_PAIR(opts, delta_intersection), + CELER_JSON_PAIR(opts, epsilon_step), + CELER_JSON_PAIR(opts, epsilon_rel_max), + CELER_JSON_PAIR(opts, errcon), + CELER_JSON_PAIR(opts, pgrow), + CELER_JSON_PAIR(opts, pshrink), + CELER_JSON_PAIR(opts, safety), + CELER_JSON_PAIR(opts, max_stepping_increase), + CELER_JSON_PAIR(opts, max_stepping_decrease), + CELER_JSON_PAIR(opts, max_nsteps), }; -#undef FDO_PAIR } //---------------------------------------------------------------------------// diff --git a/src/celeritas/field/RZMapFieldInputIO.json.cc b/src/celeritas/field/RZMapFieldInputIO.json.cc index 4796715ab1..6ecc54bdfd 100644 --- a/src/celeritas/field/RZMapFieldInputIO.json.cc +++ b/src/celeritas/field/RZMapFieldInputIO.json.cc @@ -13,6 +13,7 @@ #include #include "corecel/cont/Range.hh" +#include "corecel/io/JsonUtils.json.hh" #include "corecel/io/Logger.hh" #include "celeritas/Quantities.hh" @@ -142,20 +143,18 @@ void from_json(nlohmann::json const& j, RZMapFieldInput& inp) */ void to_json(nlohmann::json& j, RZMapFieldInput const& inp) { -#define RZFI_KEY_VALUE(NAME) {#NAME, inp.NAME} j = { {"_units", units::NativeTraits::label()}, - RZFI_KEY_VALUE(num_grid_z), - RZFI_KEY_VALUE(num_grid_r), - RZFI_KEY_VALUE(min_z), - RZFI_KEY_VALUE(min_r), - RZFI_KEY_VALUE(max_z), - RZFI_KEY_VALUE(max_r), - RZFI_KEY_VALUE(field_z), - RZFI_KEY_VALUE(field_r), - RZFI_KEY_VALUE(driver_options), + CELER_JSON_PAIR(inp, num_grid_z), + CELER_JSON_PAIR(inp, num_grid_r), + CELER_JSON_PAIR(inp, min_z), + CELER_JSON_PAIR(inp, min_r), + CELER_JSON_PAIR(inp, max_z), + CELER_JSON_PAIR(inp, max_r), + CELER_JSON_PAIR(inp, field_z), + CELER_JSON_PAIR(inp, field_r), + CELER_JSON_PAIR(inp, driver_options), }; -#undef RZFI_KEY_VALUE } //---------------------------------------------------------------------------// diff --git a/src/corecel/Macros.hh b/src/corecel/Macros.hh index 6daf9e88bf..ca6877ea26 100644 --- a/src/corecel/Macros.hh +++ b/src/corecel/Macros.hh @@ -225,6 +225,18 @@ CLS(CLS&&) = delete; \ CLS& operator=(CLS&&) = delete +/*! + * \def CELER_DEFAULT_MOVE_DELETE_COPY + * + * Explicitly declare defaulted copy and move constructors and assignment + * operators. Use this if the destructor is declared explicitly. + */ +#define CELER_DEFAULT_MOVE_DELETE_COPY(CLS) \ + CLS(CLS const&) = delete; \ + CLS& operator=(CLS const&) = delete; \ + CLS(CLS&&) = default; \ + CLS& operator=(CLS&&) = default + /*! * \def CELER_DISCARD * diff --git a/src/corecel/data/CollectionStateStore.hh b/src/corecel/data/CollectionStateStore.hh index f0d57cd950..1c6d674ef0 100644 --- a/src/corecel/data/CollectionStateStore.hh +++ b/src/corecel/data/CollectionStateStore.hh @@ -76,13 +76,8 @@ class CollectionStateStore template inline CollectionStateStore& operator=(S const& other); - //!@{ - //! Default copy/move construction/assignment - CollectionStateStore(CollectionStateStore const&) = default; - CollectionStateStore& operator=(CollectionStateStore const&) = default; - CollectionStateStore(CollectionStateStore&&) = default; - CollectionStateStore& operator=(CollectionStateStore&&) = default; - //!@} + //! Default move, delete copy (since ref "points to" val) + CELER_DEFAULT_MOVE_DELETE_COPY(CollectionStateStore); //! Whether any data is being stored explicit operator bool() const { return static_cast(val_); } diff --git a/src/corecel/io/JsonUtils.json.hh b/src/corecel/io/JsonUtils.json.hh index 3fd26ecec2..3d8f9801cc 100644 --- a/src/corecel/io/JsonUtils.json.hh +++ b/src/corecel/io/JsonUtils.json.hh @@ -70,6 +70,15 @@ OBJ[#NAME] = nullptr; \ } \ } while (0) + +/*! + * Construct a key/value pair for a JSON object. + */ +#define CELER_JSON_PAIR(STRUCT, NAME) \ + { \ + #NAME, STRUCT.NAME \ + } + //---------------------------------------------------------------------------// namespace celeritas diff --git a/src/corecel/io/detail/LoggerMessage.hh b/src/corecel/io/detail/LoggerMessage.hh index 8f5bd215b6..fc469e5454 100644 --- a/src/corecel/io/detail/LoggerMessage.hh +++ b/src/corecel/io/detail/LoggerMessage.hh @@ -41,13 +41,8 @@ class LoggerMessage // Flush message on destruction inline ~LoggerMessage(); - //!@{ //! Prevent copying but allow moving - LoggerMessage(LoggerMessage const&) = delete; - LoggerMessage& operator=(LoggerMessage const&) = delete; - LoggerMessage(LoggerMessage&&) = default; - LoggerMessage& operator=(LoggerMessage&&) = default; - //!@} + CELER_DEFAULT_MOVE_DELETE_COPY(LoggerMessage); // Write the object to the stream if applicable template diff --git a/src/corecel/math/ArraySoftUnit.hh b/src/corecel/math/ArraySoftUnit.hh new file mode 100644 index 0000000000..a3021e52b4 --- /dev/null +++ b/src/corecel/math/ArraySoftUnit.hh @@ -0,0 +1,133 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file corecel/math/ArraySoftUnit.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Types.hh" +#include "corecel/cont/Array.hh" + +#include "detail/SoftEqualTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Test for being approximately a unit vector. + * + * Consider a unit vector \em v with a small perturbation along a unit vector + * \em e : \f[ + \vec v + \epsilon \vec e + \f] + * The magnitude squared is + * \f[ + m^2 = (v + \epsilon e) \cdot (v + \epsilon e) + = v \cdot v + 2 \epsilon v \cdot e + \epsilon^2 e \cdot e + = 1 + 2 \epsilon v \cdot e + \epsilon^2 + \f] + * + * Since \f[ |v \cdot e| <= |v||e| = 1 \f] by the triangle inequality, + * then the magnitude squared of a perturbed unit vector is bounded + * \f[ + m^2 = 1 \pm 2 \epsilon + \epsilon^2 + \f] + * + * Instead of calculating the square of the tolerance we loosely bound with + * another epsilon. + */ +template +class ArraySoftUnit +{ + public: + //!@{ + //! \name Type aliases + using value_type = T; + //!@} + + public: + // Construct with explicit tolerance + CELER_FUNCTION inline ArraySoftUnit(value_type tol); + + // Construct with default tolerance + CELER_CONSTEXPR_FUNCTION ArraySoftUnit(); + + // Calculate whether the array is nearly a unit vector + template<::celeritas::size_type N> + CELER_CONSTEXPR_FUNCTION bool operator()(Array const& arr) const; + + private: + value_type tol_; +}; + +//---------------------------------------------------------------------------// +// TEMPLATE DEDUCTION GUIDES +//---------------------------------------------------------------------------// +template +CELER_FUNCTION ArraySoftUnit(T) -> ArraySoftUnit; + +//---------------------------------------------------------------------------// +// FREE FUNCTIONS +//---------------------------------------------------------------------------// +// Test for being approximately a unit vector +template +CELER_CONSTEXPR_FUNCTION bool is_soft_unit_vector(Array const& v); + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with explicit tolereance. + */ +template +CELER_FUNCTION ArraySoftUnit::ArraySoftUnit(T tol) : tol_{3 * tol} +{ + CELER_EXPECT(tol_ > 0); +} + +//---------------------------------------------------------------------------// +/*! + * Construct with default tolereance. + */ +template +CELER_CONSTEXPR_FUNCTION ArraySoftUnit::ArraySoftUnit() + : tol_{3 * detail::SoftEqualTraits::rel_prec()} +{ +} + +//---------------------------------------------------------------------------// +/*! + * Calculate whether the array is nearly a unit vector. + * + * The calculation below is equivalent to + * \code + * return SoftEqual{tol, tol}(1, dot_product(arr, arr)); + * \endcode. + */ +template +template<::celeritas::size_type N> +CELER_CONSTEXPR_FUNCTION bool +ArraySoftUnit::operator()(Array const& arr) const +{ + T length_sq{}; + for (size_type i = 0; i != N; ++i) + { + length_sq = std::fma(arr[i], arr[i], length_sq); + } + return std::fabs(length_sq - 1) < tol_ * std::fmax(real_type(1), length_sq); +} + +//---------------------------------------------------------------------------// +//! Test with default tolerance for being a unit vector +template +CELER_CONSTEXPR_FUNCTION bool is_soft_unit_vector(Array const& v) +{ + return ArraySoftUnit{}(v); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/corecel/math/ArrayUtils.hh b/src/corecel/math/ArrayUtils.hh index c4139b2187..4db5929ffd 100644 --- a/src/corecel/math/ArrayUtils.hh +++ b/src/corecel/math/ArrayUtils.hh @@ -15,7 +15,9 @@ #include "corecel/cont/Array.hh" #include "Algorithms.hh" +#include "ArraySoftUnit.hh" #include "SoftEqual.hh" + #include "detail/ArrayUtilsImpl.hh" namespace celeritas @@ -66,11 +68,6 @@ template [[nodiscard]] inline CELER_FUNCTION Array rotate(Array const& dir, Array const& rot); -//---------------------------------------------------------------------------// -// Test for being approximately a unit vector -template -inline CELER_FUNCTION bool is_soft_unit_vector(Array const& v); - //---------------------------------------------------------------------------// // INLINE DEFINITIONS //---------------------------------------------------------------------------// @@ -264,42 +261,5 @@ rotate(Array const& dir, Array const& rot) return make_unit_vector(result); } -//---------------------------------------------------------------------------// -/*! - * Test for being approximately a unit vector. - * - * Consider a unit vector \em v with a small perturbation along a unit vector - * \em e : \f[ - \vec v + \epsilon \vec e - \f] - * The magnitude squared is - * \f[ - m^2 = (v + \epsilon e) \cdot (v + \epsilon e) - = v \cdot v + 2 \epsilon v \cdot e + \epsilon^2 e \cdot e - = 1 + 2 \epsilon v \cdot e + \epsilon^2 - \f] - * - * Since \f[ |v \cdot e| <= |v||e| = 1 \f] by the triangle inequality, - * then the magnitude squared of a perturbed unit vector is bounded - * \f[ - m^2 = 1 \pm 2 \epsilon + \epsilon^2 - \f] - * - * Instead of calculating the square of the tolerance we loosely bound with - * another epsilon. - * - * Example: - * \code - CELER_EXPECT(is_soft_unit_vector(v)); - * \endcode - */ -template -CELER_FUNCTION bool is_soft_unit_vector(Array const& v) -{ - constexpr SoftEqual default_soft_eq; - SoftEqual cmp{3 * default_soft_eq.rel(), 3 * default_soft_eq.abs()}; - return cmp(T{1}, dot_product(v, v)); -} - //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/corecel/sys/ScopedMem.hh b/src/corecel/sys/ScopedMem.hh index dec32aad62..c854c199bf 100644 --- a/src/corecel/sys/ScopedMem.hh +++ b/src/corecel/sys/ScopedMem.hh @@ -58,13 +58,8 @@ class ScopedMem // Register data on destruction ~ScopedMem(); - //!@{ - //! Default move assign and construct; no copying - ScopedMem(ScopedMem&&) = default; - ScopedMem(ScopedMem const&) = delete; - ScopedMem& operator=(ScopedMem&&) = default; - ScopedMem& operator=(ScopedMem const&) = delete; - //!@} + //! Prevent copying but allow moving + CELER_DEFAULT_MOVE_DELETE_COPY(ScopedMem); private: using value_type = KibiBytes::value_type; diff --git a/test/corecel/ScopedLogStorer.hh b/test/corecel/ScopedLogStorer.hh index 15739976e4..c9e8680fd6 100644 --- a/test/corecel/ScopedLogStorer.hh +++ b/test/corecel/ScopedLogStorer.hh @@ -12,6 +12,7 @@ #include #include +#include "corecel/Macros.hh" #include "corecel/io/LoggerTypes.hh" namespace celeritas @@ -48,14 +49,6 @@ class ScopedLogStorer // Construct reference with default level explicit ScopedLogStorer(Logger* orig); - //!@{ - //! Disallow move/copy - ScopedLogStorer(ScopedLogStorer const&) = delete; - ScopedLogStorer(ScopedLogStorer&&) = delete; - ScopedLogStorer& operator=(ScopedLogStorer const&) = delete; - ScopedLogStorer& operator=(ScopedLogStorer&&) = delete; - //!@} - // Restore original logger on destruction ~ScopedLogStorer(); @@ -86,6 +79,9 @@ class ScopedLogStorer std::unique_ptr saved_logger_; VecString messages_; VecString levels_; + + //! Prevent copying and moving + CELER_DELETE_COPY_MOVE(ScopedLogStorer); }; // Print expected results From 3144f44be2fce4605b24793e49daeea4a41e9c8d Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Tue, 16 Apr 2024 15:53:10 -0500 Subject: [PATCH 04/59] Add actions and executors for generating optical distribution data (#1184) - Add pre-step gather action to cache state data needed for pre-generators - Add post-step pre-generator action to create and buffer optical distribution data --- src/celeritas/CMakeLists.txt | 2 + .../optical/detail/OpticalGenStorage.hh | 31 ++++ src/celeritas/optical/detail/PreGenAction.cc | 170 ++++++++++++++++++ src/celeritas/optical/detail/PreGenAction.cu | 74 ++++++++ src/celeritas/optical/detail/PreGenAction.hh | 112 ++++++++++++ .../optical/detail/PreGenExecutor.hh | 103 +++++++++++ .../optical/detail/PreGenGatherAction.cc | 71 ++++++++ .../optical/detail/PreGenGatherAction.cu | 41 +++++ .../optical/detail/PreGenGatherAction.hh | 65 +++++++ .../optical/detail/PreGenGatherExecutor.hh | 60 +++++++ 10 files changed, 729 insertions(+) create mode 100644 src/celeritas/optical/detail/OpticalGenStorage.hh create mode 100644 src/celeritas/optical/detail/PreGenAction.cc create mode 100644 src/celeritas/optical/detail/PreGenAction.cu create mode 100644 src/celeritas/optical/detail/PreGenAction.hh create mode 100644 src/celeritas/optical/detail/PreGenExecutor.hh create mode 100644 src/celeritas/optical/detail/PreGenGatherAction.cc create mode 100644 src/celeritas/optical/detail/PreGenGatherAction.cu create mode 100644 src/celeritas/optical/detail/PreGenGatherAction.hh create mode 100644 src/celeritas/optical/detail/PreGenGatherExecutor.hh diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 0079b78be7..f732dca5fe 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -255,6 +255,8 @@ celeritas_polysource(global/alongstep/AlongStepNeutralAction) celeritas_polysource(global/alongstep/AlongStepUniformMscAction) celeritas_polysource(global/alongstep/AlongStepRZMapFieldMscAction) celeritas_polysource(neutron/model/ChipsNeutronElasticModel) +celeritas_polysource(optical/detail/PreGenAction) +celeritas_polysource(optical/detail/PreGenGatherAction) celeritas_polysource(phys/detail/DiscreteSelectAction) celeritas_polysource(phys/detail/PreStepAction) celeritas_polysource(random/RngReseed) diff --git a/src/celeritas/optical/detail/OpticalGenStorage.hh b/src/celeritas/optical/detail/OpticalGenStorage.hh new file mode 100644 index 0000000000..8148c7160d --- /dev/null +++ b/src/celeritas/optical/detail/OpticalGenStorage.hh @@ -0,0 +1,31 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/OpticalGenStorage.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/data/StreamStore.hh" + +#include "../OpticalGenData.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +struct OpticalGenStorage +{ + using StoreT = StreamStore; + + StoreT obj; + std::vector size; +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenAction.cc b/src/celeritas/optical/detail/PreGenAction.cc new file mode 100644 index 0000000000..af060d870f --- /dev/null +++ b/src/celeritas/optical/detail/PreGenAction.cc @@ -0,0 +1,170 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenAction.cc +//---------------------------------------------------------------------------// +#include "PreGenAction.hh" + +#include + +#include "corecel/Assert.hh" +#include "celeritas/global/ActionLauncher.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/CoreTrackData.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/optical/CerenkovParams.hh" +#include "celeritas/optical/OpticalPropertyParams.hh" +#include "celeritas/optical/ScintillationParams.hh" + +#include "OpticalGenStorage.hh" +#include "PreGenExecutor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with action ID, optical properties, and storage. + */ +PreGenAction::PreGenAction(ActionId id, + SPConstProperties properties, + SPConstCerenkov cerenkov, + SPConstScintillation scintillation, + SPGenStorage storage) + : id_(id) + , properties_(std::move(properties)) + , cerenkov_(std::move(cerenkov)) + , scintillation_(std::move(scintillation)) + , storage_(std::move(storage)) +{ + CELER_EXPECT(id_); + CELER_EXPECT(scintillation_ || (cerenkov_ && properties_)); + CELER_EXPECT(storage_); +} + +//---------------------------------------------------------------------------// +/*! + * Descriptive name of the action. + */ +std::string PreGenAction::description() const +{ + return "generate Cerenkov and scintillation optical distribution data"; +} + +//---------------------------------------------------------------------------// +/*! + * Execute the action with host data. + */ +void PreGenAction::execute(CoreParams const& params, CoreStateHost& state) const +{ + this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Execute the action with device data. + */ +void PreGenAction::execute(CoreParams const& params, + CoreStateDevice& state) const +{ + this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data post-step. + * + * The distributions are stored in separate Cerenkov and scintillation buffers + * indexed by the current buffer size plus the track slot ID. The data is + * compacted at the end of each step by removing all invalid distributions. The + * order of the distributions in the buffers is guaranteed to be reproducible. + */ +template +void PreGenAction::execute_impl(CoreParams const& core_params, + CoreState& core_state) const +{ + size_type state_size = core_state.size(); + StreamId stream = core_state.stream_id(); + auto& buffer_size = storage_->size[stream.get()]; + auto const& state = storage_->obj.state(stream, state_size); + + CELER_VALIDATE(buffer_size.cerenkov + state_size <= state.cerenkov.size(), + << "insufficient capacity (" << state.cerenkov.size() + << ") for buffered Cerenkov distribution data (total " + "capacity requirement of " + << buffer_size.cerenkov + state_size << ")"); + CELER_VALIDATE( + buffer_size.scintillation + state_size <= state.scintillation.size(), + << "insufficient capacity (" << state.scintillation.size() + << ") for buffered scintillation distribution data (total " + "capacity requirement of " + << buffer_size.scintillation + state_size << ")"); + + // Generate the optical distribution data + this->pre_generate(core_params, core_state); + + // Compact the buffers + buffer_size.cerenkov = this->remove_if_invalid( + state.cerenkov, buffer_size.cerenkov, state_size, stream); + buffer_size.scintillation = this->remove_if_invalid( + state.scintillation, buffer_size.scintillation, state_size, stream); +} + +//---------------------------------------------------------------------------// +/*! + * Launch a (host) kernel to generate optical distribution data post-step. + */ +void PreGenAction::pre_generate(CoreParams const& core_params, + CoreStateHost& core_state) const +{ + TrackExecutor execute{ + core_params.ptr(), + core_state.ptr(), + detail::PreGenExecutor{properties_->host_ref(), + cerenkov_->host_ref(), + scintillation_->host_ref(), + storage_->obj.state( + core_state.stream_id(), core_state.size()), + storage_->size[core_state.stream_id().get()]}}; + launch_action(*this, core_params, core_state, execute); +} + +//---------------------------------------------------------------------------// +/*! + * Remove all invalid distributions from the buffer. + */ +size_type +PreGenAction::remove_if_invalid(ItemsRef const& buffer, + size_type offset, + size_type size, + StreamId) const +{ + auto* start = static_cast(buffer.data()); + auto* stop + = std::remove_if(start + offset, start + offset + size, IsInvalid{}); + return stop - start; +} + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +void PreGenAction::pre_generate(CoreParams const&, CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} + +size_type PreGenAction::remove_if_invalid(ItemsRef const&, + size_type, + size_type, + StreamId) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenAction.cu b/src/celeritas/optical/detail/PreGenAction.cu new file mode 100644 index 0000000000..7fe42b6ffb --- /dev/null +++ b/src/celeritas/optical/detail/PreGenAction.cu @@ -0,0 +1,74 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenAction.cu +//---------------------------------------------------------------------------// +#include "PreGenAction.hh" + +#include +#include +#include + +#include "corecel/Assert.hh" +#include "corecel/sys/ScopedProfiling.hh" +#include "corecel/sys/Thrust.device.hh" +#include "celeritas/global/ActionLauncher.device.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/optical/CerenkovParams.hh" +#include "celeritas/optical/OpticalPropertyParams.hh" +#include "celeritas/optical/ScintillationParams.hh" + +#include "OpticalGenStorage.hh" +#include "PreGenExecutor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Launch a kernel to generate optical distribution data post-step. + */ +void PreGenAction::pre_generate(CoreParams const& core_params, + CoreStateDevice& core_state) const +{ + TrackExecutor execute{ + core_params.ptr(), + core_state.ptr(), + detail::PreGenExecutor{properties_->device_ref(), + cerenkov_->device_ref(), + scintillation_->device_ref(), + storage_->obj.state( + core_state.stream_id(), core_state.size()), + storage_->size[core_state.stream_id().get()]}}; + static ActionLauncher const launch_kernel(*this); + launch_kernel(core_state, execute); +} + +//---------------------------------------------------------------------------// +/*! + * Remove all invalid distributions from the buffer. + */ +size_type +PreGenAction::remove_if_invalid(ItemsRef const& buffer, + size_type offset, + size_type size, + StreamId stream) const +{ + ScopedProfiling profile_this{"remove-if-invalid"}; + auto start = thrust::device_pointer_cast(buffer.data().get()); + auto stop = thrust::remove_if(thrust_execute_on(stream), + start + offset, + start + offset + size, + IsInvalid{}); + CELER_DEVICE_CHECK_ERROR(); + return stop - start; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenAction.hh b/src/celeritas/optical/detail/PreGenAction.hh new file mode 100644 index 0000000000..02605fb300 --- /dev/null +++ b/src/celeritas/optical/detail/PreGenAction.hh @@ -0,0 +1,112 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenAction.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Macros.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/global/ActionInterface.hh" +#include "celeritas/optical/OpticalDistributionData.hh" + +namespace celeritas +{ +class CerenkovParams; +class OpticalPropertyParams; +class ScintillationParams; + +namespace detail +{ +struct OpticalGenStorage; +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +class PreGenAction final : public ExplicitCoreActionInterface +{ + public: + //!@{ + //! \name Type aliases + using SPConstCerenkov = std::shared_ptr; + using SPConstProperties = std::shared_ptr; + using SPConstScintillation = std::shared_ptr; + using SPGenStorage = std::shared_ptr; + //!@} + + //! Check if the distribution data is valid + struct IsInvalid + { + CELER_FUNCTION bool + operator()(OpticalDistributionData const& data) const + { + return !data; + } + }; + + public: + // Construct with action ID, optical properties, and storage + PreGenAction(ActionId id, + SPConstProperties properties, + SPConstCerenkov cerenkov, + SPConstScintillation scintillation, + SPGenStorage storage); + + // Launch kernel with host data + void execute(CoreParams const&, CoreStateHost&) const final; + + // Launch kernel with device data + void execute(CoreParams const&, CoreStateDevice&) const final; + + //! ID of the model + ActionId action_id() const final { return id_; } + + //! Short name for the action + std::string label() const final { return "optical-pre-generator-post"; } + + // Name of the action (for user output) + std::string description() const final; + + //! Dependency ordering of the action + ActionOrder order() const final { return ActionOrder::post_post; } + + private: + //// TYPES //// + + template + using ItemsRef + = Collection; + + //// DATA //// + + ActionId id_; + SPConstProperties properties_; + SPConstCerenkov cerenkov_; + SPConstScintillation scintillation_; + SPGenStorage storage_; + + //// HELPER FUNCTIONS //// + + template + void execute_impl(CoreParams const&, CoreState&) const; + + void pre_generate(CoreParams const&, CoreStateHost&) const; + void pre_generate(CoreParams const&, CoreStateDevice&) const; + + size_type remove_if_invalid(ItemsRef const&, + size_type, + size_type, + StreamId) const; + size_type remove_if_invalid(ItemsRef const&, + size_type, + size_type, + StreamId) const; +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenExecutor.hh b/src/celeritas/optical/detail/PreGenExecutor.hh new file mode 100644 index 0000000000..75423429c7 --- /dev/null +++ b/src/celeritas/optical/detail/PreGenExecutor.hh @@ -0,0 +1,103 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "celeritas/global/CoreTrackView.hh" +#include "celeritas/optical/CerenkovPreGenerator.hh" +#include "celeritas/optical/OpticalGenData.hh" +#include "celeritas/optical/ScintillationPreGenerator.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +// LAUNCHER +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +struct PreGenExecutor +{ + inline CELER_FUNCTION void + operator()(celeritas::CoreTrackView const& track); + + NativeCRef const properties; + NativeCRef const cerenkov; + NativeCRef const scintillation; + NativeRef const state; + OpticalBufferSize size; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +CELER_FUNCTION void PreGenExecutor::operator()(CoreTrackView const& track) +{ + CELER_EXPECT(state); + + using DistId = ItemId; + + auto tsid = track.track_slot_id(); + auto& cerenkov_dist = state.cerenkov[DistId(size.cerenkov + tsid.get())]; + auto& scintillation_dist + = state.scintillation[DistId(size.scintillation + tsid.get())]; + + // Clear distribution data + cerenkov_dist = {}; + scintillation_dist = {}; + + auto sim = track.make_sim_view(); + bool inactive = sim.status() == TrackStatus::inactive; + auto optmat_id = inactive ? OpticalMaterialId{} + : track.make_material_view() + .make_material_view() + .optical_material_id(); + + if (inactive || !optmat_id || sim.step_length() == 0) + { + // Inactive tracks, materials with no optical properties, or particles + // that started the step with zero energy (e.g. a stopped positron) + return; + } + + Real3 const& pos = track.make_geo_view().pos(); + auto particle = track.make_particle_view(); + auto rng = track.make_rng_engine(); + + // Get the distribution data used to generate scintillation and Cerenkov + // optical photons + if (cerenkov && particle.charge() != zero_quantity()) + { + CELER_ASSERT(properties); + CerenkovPreGenerator generate(particle, + sim, + pos, + optmat_id, + properties, + cerenkov, + state.step[tsid]); + cerenkov_dist = generate(rng); + } + if (scintillation) + { + auto edep = track.make_physics_step_view().energy_deposition(); + ScintillationPreGenerator generate( + particle, sim, pos, optmat_id, edep, scintillation, state.step[tsid]); + scintillation_dist = generate(rng); + } +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenGatherAction.cc b/src/celeritas/optical/detail/PreGenGatherAction.cc new file mode 100644 index 0000000000..da332bc24e --- /dev/null +++ b/src/celeritas/optical/detail/PreGenGatherAction.cc @@ -0,0 +1,71 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenGatherAction.cc +//---------------------------------------------------------------------------// +#include "PreGenGatherAction.hh" + +#include + +#include "corecel/Assert.hh" +#include "celeritas/global/ActionLauncher.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/CoreTrackData.hh" +#include "celeritas/global/TrackExecutor.hh" + +#include "OpticalGenStorage.hh" +#include "PreGenGatherExecutor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with action ID, optical properties, and storage. + */ +PreGenGatherAction::PreGenGatherAction(ActionId id, SPGenStorage storage) + : id_(id), storage_(std::move(storage)) +{ + CELER_EXPECT(id_); + CELER_EXPECT(storage_); +} + +//---------------------------------------------------------------------------// +/*! + * Descriptive name of the action. + */ +std::string PreGenGatherAction::description() const +{ + return "gather pre-step data to generate optical distributions"; +} + +//---------------------------------------------------------------------------// +/*! + * Gather pre-step data. + */ +void PreGenGatherAction::execute(CoreParams const& params, + CoreStateHost& state) const +{ + auto execute = make_active_track_executor( + params.ptr(), + state.ptr(), + detail::PreGenGatherExecutor{storage_->obj.state( + state.stream_id(), state.size())}); + launch_action(*this, params, state, execute); +} + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +void PreGenGatherAction::execute(CoreParams const&, CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenGatherAction.cu b/src/celeritas/optical/detail/PreGenGatherAction.cu new file mode 100644 index 0000000000..30761b70af --- /dev/null +++ b/src/celeritas/optical/detail/PreGenGatherAction.cu @@ -0,0 +1,41 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenGatherAction.cu +//---------------------------------------------------------------------------// +#include "PreGenGatherAction.hh" + +#include "corecel/Assert.hh" +#include "celeritas/global/ActionLauncher.device.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/TrackExecutor.hh" + +#include "OpticalGenStorage.hh" +#include "PreGenGatherExecutor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Gather pre-step data. + */ +void PreGenGatherAction::execute(CoreParams const& params, + CoreStateDevice& state) const +{ + auto execute = make_active_track_executor( + params.ptr(), + state.ptr(), + detail::PreGenGatherExecutor{storage_->obj.state( + state.stream_id(), state.size())}); + static ActionLauncher const launch_kernel(*this); + launch_kernel(state, execute); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenGatherAction.hh b/src/celeritas/optical/detail/PreGenGatherAction.hh new file mode 100644 index 0000000000..56ea88f3b7 --- /dev/null +++ b/src/celeritas/optical/detail/PreGenGatherAction.hh @@ -0,0 +1,65 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenGatherAction.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "corecel/Macros.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/global/ActionInterface.hh" + +namespace celeritas +{ +namespace detail +{ +struct OpticalGenStorage; +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +class PreGenGatherAction final : public ExplicitCoreActionInterface +{ + public: + //!@{ + //! \name Type aliases + using SPGenStorage = std::shared_ptr; + //!@} + + public: + // Construct with action ID and storage + PreGenGatherAction(ActionId id, SPGenStorage storage); + + // Launch kernel with host data + void execute(CoreParams const&, CoreStateHost&) const final; + + // Launch kernel with device data + void execute(CoreParams const&, CoreStateDevice&) const final; + + //! ID of the model + ActionId action_id() const final { return id_; } + + //! Short name for the action + std::string label() const final { return "optical-pre-generator-pre"; } + + // Name of the action (for user output) + std::string description() const final; + + //! Dependency ordering of the action + ActionOrder order() const final { return ActionOrder::pre; } + + private: + //// DATA //// + + ActionId id_; + SPGenStorage storage_; +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenGatherExecutor.hh b/src/celeritas/optical/detail/PreGenGatherExecutor.hh new file mode 100644 index 0000000000..4224cdf8a3 --- /dev/null +++ b/src/celeritas/optical/detail/PreGenGatherExecutor.hh @@ -0,0 +1,60 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/PreGenGatherExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "celeritas/global/CoreTrackView.hh" +#include "celeritas/optical/OpticalGenData.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +// LAUNCHER +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +struct PreGenGatherExecutor +{ + inline CELER_FUNCTION void + operator()(celeritas::CoreTrackView const& track); + + NativeRef const state; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Gather pre-step data needed to generate optical distributions. + */ +CELER_FUNCTION void PreGenGatherExecutor::operator()(CoreTrackView const& track) +{ + CELER_EXPECT(state); + + auto particle = track.make_particle_view(); + if (particle.is_stopped()) + { + return; + } + + CELER_ASSERT(track.track_slot_id() < state.step.size()); + OpticalPreStepData& step = state.step[track.track_slot_id()]; + step.speed = particle.speed(); + step.pos = track.make_geo_view().pos(); + step.time = track.make_sim_view().time(); + + CELER_ENSURE(step); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas From e1c7aedce274dece374ffeb8e093c1793d5b2e77 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 16 Apr 2024 20:11:53 -0400 Subject: [PATCH 05/59] Pin sphinx at 7.2 to fix user doc build (#1188) --- scripts/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 4554dd4f26..22c87b37b9 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,4 +1,4 @@ -Sphinx>=4.0.0 +Sphinx >= 4, < 7.3 breathe furo sphinxcontrib-bibtex From fe2611587b4b4a1e4f6a71e33d3ebcb6e4332041 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 17 Apr 2024 10:31:39 -0400 Subject: [PATCH 06/59] Complete GDML-to-ORANGE geometry converter (#1180) * Add geometry converter * Add ORANGE geometry constructor * Don't inline child if it's transformed --- app/celer-g4/GlobalSetup.cc | 8 - app/celer-g4/test-harness.py | 7 +- src/accel/detail/HitProcessor.cc | 2 - src/orange/CMakeLists.txt | 1 + src/orange/OrangeParams.cc | 64 ++++-- src/orange/OrangeParams.hh | 4 +- src/orange/g4org/Converter.cc | 73 +++++++ src/orange/g4org/Converter.hh | 97 +++++++++ src/orange/g4org/PhysicalVolumeConverter.cc | 1 - src/orange/g4org/ProtoConstructor.cc | 7 +- src/orange/orangeinp/UnitProto.cc | 9 + test/orange/CMakeLists.txt | 1 + test/orange/Orange.test.cc | 214 ++++++++++++++++++++ test/orange/OrangeGeoTestBase.cc | 19 ++ test/orange/OrangeGeoTestBase.hh | 3 + test/orange/g4org/Converter.test.cc | 139 +++++++++++++ test/orange/g4org/ProtoConstructor.test.cc | 56 +++-- 17 files changed, 642 insertions(+), 63 deletions(-) create mode 100644 src/orange/g4org/Converter.cc create mode 100644 src/orange/g4org/Converter.hh create mode 100644 test/orange/g4org/Converter.test.cc diff --git a/app/celer-g4/GlobalSetup.cc b/app/celer-g4/GlobalSetup.cc index 4880d5377c..36f4648c28 100644 --- a/app/celer-g4/GlobalSetup.cc +++ b/app/celer-g4/GlobalSetup.cc @@ -137,14 +137,6 @@ void GlobalSetup::ReadInput(std::string const& filename) CELER_NOT_CONFIGURED("nlohmann_json"); #endif - // Input options - if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE) - { - // To allow ORANGE to work for testing purposes, pass the GDML - // input filename to Celeritas - options_->geometry_file = input_.geometry_file; - } - // Output options options_->output_file = input_.output_file; options_->physics_output_file = input_.physics_output_file; diff --git a/app/celer-g4/test-harness.py b/app/celer-g4/test-harness.py index 8dfae72834..eb57f6feac 100755 --- a/app/celer-g4/test-harness.py +++ b/app/celer-g4/test-harness.py @@ -114,12 +114,7 @@ def strtobool(text): j = json.loads(out_text) except json.decoder.JSONDecodeError as e: print(f"error ({e}): expected a JSON object but got the following stdout:") - with open(out_file, 'w') as f: - f.write(out_text) - print("Wrote text to", out_file) - if result.returncode: - print("fatal:", str(e)) - exit(result.returncode) + print(out_text) else: with open(out_file, 'w') as f: json.dump(j, f, indent=1) diff --git a/src/accel/detail/HitProcessor.cc b/src/accel/detail/HitProcessor.cc index e3fca8fc5e..8e1d1bef75 100644 --- a/src/accel/detail/HitProcessor.cc +++ b/src/accel/detail/HitProcessor.cc @@ -109,8 +109,6 @@ HitProcessor::HitProcessor(SPConstVecLV detector_volumes, tracks_.back()->SetParentID(-1); } - // Create secondary vector if using track data - // Convert logical volumes (global) to sensitive detectors (thread local) CELER_LOG_LOCAL(debug) << "Setting up " << detector_volumes_->size() << " sensitive detectors"; diff --git a/src/orange/CMakeLists.txt b/src/orange/CMakeLists.txt index 1d2098a55b..82de4e8304 100644 --- a/src/orange/CMakeLists.txt +++ b/src/orange/CMakeLists.txt @@ -84,6 +84,7 @@ endif() if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") set( _cg4org_sources + g4org/Converter.cc g4org/LogicalVolumeConverter.cc g4org/PhysicalVolumeConverter.cc g4org/ProtoConstructor.cc diff --git a/src/orange/OrangeParams.cc b/src/orange/OrangeParams.cc index 3fb95944e9..306ed71581 100644 --- a/src/orange/OrangeParams.cc +++ b/src/orange/OrangeParams.cc @@ -25,10 +25,12 @@ #include "corecel/io/ScopedTimeLog.hh" #include "corecel/io/StringUtils.hh" #include "geocel/BoundingBox.hh" +#include "geocel/GeantGeoUtils.hh" #include "OrangeData.hh" // IWYU pragma: associated #include "OrangeInput.hh" #include "OrangeTypes.hh" +#include "g4org/Converter.hh" #include "univ/detail/LogicStack.hh" #include "detail/DepthCalculator.hh" @@ -48,7 +50,7 @@ namespace { //---------------------------------------------------------------------------// /*! - * Load a geometry from the given filename. + * Load a geometry from the given JSON file. */ OrangeInput input_from_json(std::string filename) { @@ -58,18 +60,6 @@ OrangeInput input_from_json(std::string filename) CELER_LOG(info) << "Loading ORANGE geometry from JSON at " << filename; ScopedTimeLog scoped_time; - if (ends_with(filename, ".gdml")) - { - CELER_LOG(warning) << "Using ORANGE geometry with GDML suffix: trying " - "`.org.json` instead"; - filename.erase(filename.end() - 5, filename.end()); - filename += ".org.json"; - } - else if (!ends_with(filename, ".json")) - { - CELER_LOG(warning) << "Expected '.json' extension for JSON input"; - } - OrangeInput result; #if CELERITAS_USE_JSON @@ -83,6 +73,40 @@ OrangeInput input_from_json(std::string filename) return result; } +//---------------------------------------------------------------------------// +/*! + * Load a geometry from the given filename. + */ +OrangeInput input_from_file(std::string filename) +{ + if (ends_with(filename, ".gdml")) + { + if (CELERITAS_USE_GEANT4) + { + // Load with Geant4: must *not* be using run manager + auto* world = ::celeritas::load_geant_geometry_native(filename); + auto result = g4org::Converter{}(world).input; + ::celeritas::reset_geant_geometry(); + return result; + } + else + { + CELER_LOG(warning) << "Using ORANGE geometry with GDML suffix " + "when Geant4 is disabled: trying " + "`.org.json` instead"; + filename.erase(filename.end() - 5, filename.end()); + filename += ".org.json"; + } + } + else + { + CELER_VALIDATE(ends_with(filename, ".json"), + << "expected JSON extension for ORANGE input '" + << filename << "'"); + } + return input_from_json(std::move(filename)); +} + //---------------------------------------------------------------------------// } // namespace @@ -93,20 +117,20 @@ OrangeInput input_from_json(std::string filename) * The JSON format is defined by the SCALE ORANGE exporter (not currently * distributed). */ -OrangeParams::OrangeParams(std::string const& json_filename) - : OrangeParams(input_from_json(json_filename)) +OrangeParams::OrangeParams(std::string const& filename) + : OrangeParams(input_from_file(filename)) { } //---------------------------------------------------------------------------// /*! - * Construct in-memory from a Geant4 geometry (not implemented). + * Construct in-memory from a Geant4 geometry. * - * Perhaps someday we'll implement in-memory translation... + * TODO: expose options? Fix volume mappings? */ -OrangeParams::OrangeParams(G4VPhysicalVolume const*) +OrangeParams::OrangeParams(G4VPhysicalVolume const* world) + : OrangeParams(std::move(g4org::Converter{}(world).input)) { - CELER_NOT_IMPLEMENTED("Geant4->VecGeom geometry translation"); } //---------------------------------------------------------------------------// @@ -118,8 +142,6 @@ OrangeParams::OrangeParams(G4VPhysicalVolume const*) OrangeParams::OrangeParams(OrangeInput&& input) { CELER_VALIDATE(input, << "input geometry is incomplete"); - CELER_VALIDATE(!input.universes.empty(), - << "input geometry has no universes"); // Save global bounding box bbox_ = [&input] { diff --git a/src/orange/OrangeParams.hh b/src/orange/OrangeParams.hh index 2911ab8afe..0f1552b104 100644 --- a/src/orange/OrangeParams.hh +++ b/src/orange/OrangeParams.hh @@ -38,8 +38,8 @@ class OrangeParams final : public GeoParamsSurfaceInterface, public ParamsDataInterface { public: - // Construct from a JSON file (if JSON is enabled) - explicit OrangeParams(std::string const& json_filename); + // Construct from a JSON or GDML file (if JSON or Geant4 are enabled) + explicit OrangeParams(std::string const& filename); // Construct in-memory from Geant4 (not implemented) explicit OrangeParams(G4VPhysicalVolume const*); diff --git a/src/orange/g4org/Converter.cc b/src/orange/g4org/Converter.cc new file mode 100644 index 0000000000..79e225e012 --- /dev/null +++ b/src/orange/g4org/Converter.cc @@ -0,0 +1,73 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/g4org/Converter.cc +//---------------------------------------------------------------------------// +#include "Converter.hh" + +#include "corecel/io/Logger.hh" +#include "geocel/detail/LengthUnits.hh" + +#include "PhysicalVolumeConverter.hh" +#include "ProtoConstructor.hh" + +namespace celeritas +{ +namespace g4org +{ +//---------------------------------------------------------------------------// +/*! + * Construct with options. + */ +Converter::Converter(Options&& opts) : opts_{std::move(opts)} +{ + if (!opts_.tol) + { + opts_.tol = Tolerance<>::from_default(lengthunits::millimeter); + } + + if (real_type{1} - ipow<2>(opts_.tol.rel) == real_type{1}) + { + CELER_LOG(warning) + << "Requested relative tolerance (" << opts_.tol.rel + << ") for ORANGE is very small: tracking errors may result due to " + "incomplete geometry simplification"; + } + + CELER_ENSURE(opts_.tol); +} + +//---------------------------------------------------------------------------// +/*! + * Convert the world. + */ +auto Converter::operator()(arg_type g4world) -> result_type +{ + CELER_EXPECT(g4world); + + CELER_LOG(debug) << "Converting Geant4 geometry elements"; + + // Convert solids, logical volumes, physical volumes + PhysicalVolumeConverter::Options options; + options.verbose = opts_.verbose; + PhysicalVolumeConverter convert_pv(std::move(options)); + PhysicalVolume world = convert_pv(*g4world); + CELER_VALIDATE(std::holds_alternative(world.transform), + << "world volume should not have a transformation"); + + CELER_LOG(debug) << "Building protos"; + // Convert logical volumes into protos + auto global_proto = ProtoConstructor{opts_.verbose}(*world.lv); + + CELER_LOG(debug) << "Building universes"; + // Build universes from protos + result_type result; + result.input = build_input(opts_.tol, *global_proto); + return result; +} + +//---------------------------------------------------------------------------// +} // namespace g4org +} // namespace celeritas diff --git a/src/orange/g4org/Converter.hh b/src/orange/g4org/Converter.hh new file mode 100644 index 0000000000..723085d739 --- /dev/null +++ b/src/orange/g4org/Converter.hh @@ -0,0 +1,97 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2023-2024 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/g4org/Converter.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "celeritas_config.h" +#include "orange/OrangeInput.hh" +#include "orange/OrangeTypes.hh" + +//---------------------------------------------------------------------------// +// Forward declarations +//---------------------------------------------------------------------------// + +class G4LogicalVolume; +class G4VPhysicalVolume; + +namespace celeritas +{ +namespace g4org +{ +//---------------------------------------------------------------------------// +/*! + * Create an ORANGE geometry model from an in-memory Geant4 model. + * + * Return the new world volume and a mapping of Geant4 logical volumes to + * VecGeom-based volume IDs. + * + * The default Geant4 "tolerance" (often used as surface "thickness") is 1e-9 + * mm, and the relative tolerance when specifying a length scale is 1e-11 (so + * the default macro length scale is expected to be 100 mm = 10 cm). + * That relative tolerance is *much* too small for any quadric operations or + * angular rotations to be differentiated, so for now we'll stick with the + * ORANGE default tolerance of 1e-8 relative, and we assume a 1mm length scale. + */ +class Converter +{ + public: + //!@{ + //! \name Type aliases + using arg_type = G4VPhysicalVolume const*; + using MapLvVolId = std::unordered_map; + //!@} + + //! Input options for the conversion + struct Options + { + //! Write output about volumes being converted + bool verbose{false}; + //! Manually specify a tracking/construction tolerance + Tolerance<> tol; + }; + + struct result_type + { + OrangeInput input; + MapLvVolId volumes; //! TODO + }; + + public: + // Construct with options + explicit Converter(Options&&); + + //! Construct with default options + Converter() : Converter{Options{}} {} + + // Convert the world + result_type operator()(arg_type); + + private: + Options opts_; +}; + +//---------------------------------------------------------------------------// + +#if !(CELERITAS_USE_GEANT4 \ + && CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) +inline Converter::Converter(Options&&) +{ + CELER_DISCARD(opts_); +} + +inline auto Converter::operator()(arg_type) -> result_type +{ + CELER_NOT_CONFIGURED("Geant4 with double-precision real_type"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace g4org +} // namespace celeritas diff --git a/src/orange/g4org/PhysicalVolumeConverter.cc b/src/orange/g4org/PhysicalVolumeConverter.cc index eebd1a249c..3b02bc5cd3 100644 --- a/src/orange/g4org/PhysicalVolumeConverter.cc +++ b/src/orange/g4org/PhysicalVolumeConverter.cc @@ -93,7 +93,6 @@ auto PhysicalVolumeConverter::operator()(arg_type g4world) -> result_type CELER_EXPECT(!g4world.GetRotation()); CELER_EXPECT(g4world.GetTranslation() == G4ThreeVector(0, 0, 0)); - CELER_LOG(status) << "Converting Geant4 geometry"; ScopedProfiling profile_this{"import-geant-geo"}; ScopedMem record_mem("PhysicalVolumeConverter.convert"); ScopedTimeLog scoped_time; diff --git a/src/orange/g4org/ProtoConstructor.cc b/src/orange/g4org/ProtoConstructor.cc index 0521ca646d..3fa044af27 100644 --- a/src/orange/g4org/ProtoConstructor.cc +++ b/src/orange/g4org/ProtoConstructor.cc @@ -159,9 +159,11 @@ void ProtoConstructor::place_pv(VariantTransform const& parent_transform, add_material( Transformed::or_object(pv.lv->solid, std::move(transform))); } - else if (pv.lv.use_count() == 1) + else if (pv.lv.use_count() == 1 + && std::holds_alternative(pv.transform)) { // Child can be inlined into the parent because it's used only once + // *and* it doesn't have a transform relative to the parent if (CELER_UNLIKELY(verbose_)) { std::clog << std::string(depth_, ' ') << " -> " @@ -213,7 +215,8 @@ void ProtoConstructor::place_pv(VariantTransform const& parent_transform, { std::clog << std::string(depth_, ' ') << " : " << "daughter shape is " - << to_string(*proto->daughters.back().make_interior()); + << to_string(*proto->daughters.back().make_interior()) + << std::endl; } } } diff --git a/src/orange/orangeinp/UnitProto.cc b/src/orange/orangeinp/UnitProto.cc index 6d78cc9bdc..ed2171eaf3 100644 --- a/src/orange/orangeinp/UnitProto.cc +++ b/src/orange/orangeinp/UnitProto.cc @@ -10,6 +10,7 @@ #include #include +#include "corecel/io/Logger.hh" #include "orange/OrangeData.hh" #include "orange/OrangeInput.hh" @@ -104,6 +105,9 @@ void UnitProto::build(InputBuilder& input) const // Get the list of all surfaces actually used auto const sorted_local_surfaces = calc_surfaces(csg_unit.tree); + CELER_LOG(debug) << "...built " << this->label() << ": used " + << sorted_local_surfaces.size() << " of " + << csg_unit.surfaces.size() << " surfaces"; UnitInput result; result.label = input_.label; @@ -291,6 +295,11 @@ void UnitProto::build(InputBuilder& input) const auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit { CELER_EXPECT(tol); + + CELER_LOG(debug) << "Building " << this->label() << ": " + << input_.daughters.size() << " daughters and " + << input_.materials.size() << " materials..."; + detail::CsgUnit result; detail::CsgUnitBuilder unit_builder(&result, tol); diff --git a/test/orange/CMakeLists.txt b/test/orange/CMakeLists.txt index 6d39a02cab..12d0e91a4e 100644 --- a/test/orange/CMakeLists.txt +++ b/test/orange/CMakeLists.txt @@ -130,6 +130,7 @@ celeritas_add_test(univ/detail/SenseCalculator.test.cc) if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") set(CELERITASTEST_PREFIX orange/g4org) + celeritas_add_test(g4org/Converter.test.cc) celeritas_add_test(g4org/SolidConverter.test.cc LINK_LIBRARIES ${_g4_geo_libs}) celeritas_add_test(g4org/PhysicalVolumeConverter.test.cc) diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index b1c5354787..33826d681f 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -10,6 +10,7 @@ #include "corecel/math/Algorithms.hh" #include "geocel/Types.hh" +#include "geocel/detail/LengthUnits.hh" #include "orange/OrangeInput.hh" #include "orange/OrangeParams.hh" #include "orange/OrangeParamsOutput.hh" @@ -51,6 +52,25 @@ class JsonOrangeTest : public OrangeTest } }; +class GeantOrangeTest : public OrangeTest +{ + public: + void SetUp() final + { + if (!CELERITAS_USE_GEANT4) + { + GTEST_SKIP() << "Geant4 is disabled"; + } + if (CELERITAS_REAL_TYPE != CELERITAS_REAL_TYPE_DOUBLE) + { + GTEST_SKIP() << "Converting Geant4 requires double-precision " + "reals"; + } + this->build_gdml_geometry(this->geometry_basename() + ".gdml"); + } + real_type unit_length() const override { return lengthunits::centimeter; } +}; + class ShiftTrackerTest : public JsonOrangeTest { protected: @@ -1403,6 +1423,200 @@ TEST_F(InputBuilderTest, hierarchy) } } +//---------------------------------------------------------------------------// +class TestEm3GeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "testem3"; } +}; + +TEST_F(TestEm3GeantTest, trace) +{ + { + auto result = this->track({-20.1}, {1, 0, 0}); + + static char const* const expected_volumes[] + = {"World", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "World"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = { + 0.1, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 4}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.050, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 2}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +class TestEm3FlatGeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "testem3-flat"; } +}; + +TEST_F(TestEm3FlatGeantTest, trace) +{ + { + auto result = this->track({-20.1}, {1, 0, 0}); + + static char const* const expected_volumes[] + = {"world", "gap_0", "absorber_0", "gap_1", + "absorber_1", "gap_2", "absorber_2", "gap_3", + "absorber_3", "gap_4", "absorber_4", "gap_5", + "absorber_5", "gap_6", "absorber_6", "gap_7", + "absorber_7", "gap_8", "absorber_8", "gap_9", + "absorber_9", "gap_10", "absorber_10", "gap_11", + "absorber_11", "gap_12", "absorber_12", "gap_13", + "absorber_13", "gap_14", "absorber_14", "gap_15", + "absorber_15", "gap_16", "absorber_16", "gap_17", + "absorber_17", "gap_18", "absorber_18", "gap_19", + "absorber_19", "gap_20", "absorber_20", "gap_21", + "absorber_21", "gap_22", "absorber_22", "gap_23", + "absorber_23", "gap_24", "absorber_24", "gap_25", + "absorber_25", "gap_26", "absorber_26", "gap_27", + "absorber_27", "gap_28", "absorber_28", "gap_29", + "absorber_29", "gap_30", "absorber_30", "gap_31", + "absorber_31", "gap_32", "absorber_32", "gap_33", + "absorber_33", "gap_34", "absorber_34", "gap_35", + "absorber_35", "gap_36", "absorber_36", "gap_37", + "absorber_37", "gap_38", "absorber_38", "gap_39", + "absorber_39", "gap_40", "absorber_40", "gap_41", + "absorber_41", "gap_42", "absorber_42", "gap_43", + "absorber_43", "gap_44", "absorber_44", "gap_45", + "absorber_45", "gap_46", "absorber_46", "gap_47", + "absorber_47", "gap_48", "absorber_48", "gap_49", + "absorber_49", "world"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = { + 0.1, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 4}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.05, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 2}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +class SimpleCmsGeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "simple-cms"; } +}; + +TEST_F(SimpleCmsGeantTest, trace) +{ + { + auto result = this->track({-75, 0, 0}, {1, 0, 0}); + static char const* const expected_volumes[] = {"si_tracker", + "vacuum_tube", + "si_tracker", + "em_calorimeter", + "had_calorimeter", + "sc_solenoid", + "fe_muon_chambers", + "world"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] + = {45, 60, 95, 50, 100, 100, 325, 300}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {22.5, 700, 47.5, 25, 50, 50, 162.5, 150}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + auto result = this->track({25, 0, 701}, {0, 0, -1}); + static char const* const expected_volumes[] + = {"world", "vacuum_tube", "world"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 1400, 1300}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {0.5, 5, 5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +class ZnenvGeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "znenv"; } +}; + +TEST_F(ZnenvGeantTest, trace) +{ + static char const* const expected_mid_volumes[] + = {"World", "ZNENV", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", + "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", + "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", + "ZNST", "ZNST", "ZNST", "ZNENV", "World"}; + static real_type const expected_mid_distances[] + = {6.38, 0.1, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, + 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, + 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.1, 46.38}; + { + auto result = this->track({-10, 0.0001, 0}, {1, 0, 0}); + EXPECT_VEC_EQ(expected_mid_volumes, result.volumes); + EXPECT_VEC_SOFT_EQ(expected_mid_distances, result.distances); + } + { + auto result = this->track({0.0001, -10, 0}, {0, 1, 0}); + EXPECT_VEC_EQ(expected_mid_volumes, result.volumes); + EXPECT_VEC_SOFT_EQ(expected_mid_distances, result.distances); + } +} + //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas diff --git a/test/orange/OrangeGeoTestBase.cc b/test/orange/OrangeGeoTestBase.cc index 7ffc5ea9a8..e3d476daf5 100644 --- a/test/orange/OrangeGeoTestBase.cc +++ b/test/orange/OrangeGeoTestBase.cc @@ -71,6 +71,25 @@ std::vector OrangeGeoTestBase::string_to_senses(std::string const& s) return result; } +//---------------------------------------------------------------------------// +/*! + * Load `test/geocel/data/{filename}` GDML input using Geant4. + */ +void OrangeGeoTestBase::build_gdml_geometry(std::string const& filename) +{ + CELER_EXPECT(!params_); + CELER_VALIDATE(CELERITAS_USE_GEANT4, + << "Geant4 is not enabled so geometry cannot be loaded"); + + ScopedLogStorer scoped_log_{&celeritas::world_logger()}; + params_ + = std::make_unique(this->test_data_path("geocel", filename)); + + static std::string const expected_log_levels[] = {"info"}; + EXPECT_VEC_EQ(expected_log_levels, scoped_log_.levels()) << scoped_log_; + ASSERT_TRUE(this->geometry()); +} + //---------------------------------------------------------------------------// /*! * Load a geometry from the given JSON filename. diff --git a/test/orange/OrangeGeoTestBase.hh b/test/orange/OrangeGeoTestBase.hh index 6c4738f9ba..170506d67a 100644 --- a/test/orange/OrangeGeoTestBase.hh +++ b/test/orange/OrangeGeoTestBase.hh @@ -65,6 +65,9 @@ class OrangeGeoTestBase : public OrangeTestBase // Convert a string to a sense vector static std::vector string_to_senses(std::string const& s); + // Load `test/geocel/data/{filename}` GDML input using Geant4 + void build_gdml_geometry(std::string const& filename); + // Load `test/orange/data/{filename}` JSON input void build_geometry(std::string const& filename); diff --git a/test/orange/g4org/Converter.test.cc b/test/orange/g4org/Converter.test.cc new file mode 100644 index 0000000000..2eaec2fd92 --- /dev/null +++ b/test/orange/g4org/Converter.test.cc @@ -0,0 +1,139 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/g4org/Converter.test.cc +//---------------------------------------------------------------------------// +#include "orange/g4org/Converter.hh" + +#include + +#include "corecel/ScopedLogStorer.hh" +#include "corecel/io/Logger.hh" +#include "corecel/sys/Environment.hh" +#include "geocel/GeantGeoUtils.hh" +#include "geocel/UnitUtils.hh" +#include "orange/OrangeInput.hh" + +#include "celeritas_test.hh" + +using namespace celeritas::test; + +namespace celeritas +{ +namespace g4org +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class ConverterTest : public ::celeritas::test::Test +{ + protected: + //! Helper function: build via Geant4 GDML reader + G4VPhysicalVolume const* load(std::string const& filename) + { + CELER_EXPECT(!filename.empty()); + if (filename == loaded_filename_) + { + return world_volume_; + } + + if (world_volume_) + { + // Clear old geant4 data + TearDownTestSuite(); + } + ScopedLogStorer scoped_log_{&celeritas::self_logger(), + LogLevel::warning}; + world_volume_ = ::celeritas::load_geant_geometry_native(filename); + EXPECT_TRUE(scoped_log_.empty()) << scoped_log_; + loaded_filename_ = filename; + + return world_volume_; + } + + G4VPhysicalVolume const* load_test_gdml(std::string_view basename) + { + return this->load( + this->test_data_path("geocel", std::string(basename) + ".gdml")); + } + + static void TearDownTestSuite() + { + loaded_filename_ = {}; + ::celeritas::reset_geant_geometry(); + world_volume_ = nullptr; + } + + private: + static std::string loaded_filename_; + static G4VPhysicalVolume* world_volume_; +}; + +std::string ConverterTest::loaded_filename_{}; +G4VPhysicalVolume* ConverterTest::world_volume_{nullptr}; + +//---------------------------------------------------------------------------// +TEST_F(ConverterTest, testem3) +{ + Converter convert; + auto result = convert(this->load_test_gdml("testem3")).input; + + ASSERT_EQ(2, result.universes.size()); + if (auto* unit = std::get_if(&result.universes[0])) + { + SCOPED_TRACE("universe 0"); + EXPECT_EQ("World0x0", this->genericize_pointers(unit->label.name)); + EXPECT_EQ(53, unit->volumes.size()); + EXPECT_EQ(61, unit->surfaces.size()); + EXPECT_VEC_SOFT_EQ((Real3{-24, -24, -24}), to_cm(unit->bbox.lower())); + EXPECT_VEC_SOFT_EQ((Real3{24, 24, 24}), to_cm(unit->bbox.upper())); + } + else + { + FAIL() << "wrong universe variant"; + } + + if (auto* unit = std::get_if(&result.universes[1])) + { + SCOPED_TRACE("universe 1"); + EXPECT_EQ("Layer0x0", genericize_pointers(unit->label.name)); + EXPECT_EQ(4, unit->volumes.size()); + EXPECT_EQ(1, unit->surfaces.size()); + EXPECT_VEC_SOFT_EQ((Real3{-0.4, -20, -20}), to_cm(unit->bbox.lower())); + EXPECT_VEC_SOFT_EQ((Real3{0.4, 20, 20}), to_cm(unit->bbox.upper())); + } + else + { + FAIL() << "wrong universe variant"; + } +} + +TEST_F(ConverterTest, DISABLED_arbitrary) +{ + std::string filename = celeritas::getenv("GDML"); + CELER_VALIDATE(!filename.empty(), + << "Set the 'GDML' environment variable and run this " + "test with " + "--gtest_filter=*arbitrary " + "--gtest_also_run_disabled_tests"); + + Converter convert([] { + Converter::Options opts; + opts.verbose = false; + return opts; + }()); + auto input = convert(this->load(filename)).input; + + filename += ".json"; + CELER_LOG(info) << "Writing JSON translation to " << filename; + std::ofstream os(filename); + os << input; +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace g4org +} // namespace celeritas diff --git a/test/orange/g4org/ProtoConstructor.test.cc b/test/orange/g4org/ProtoConstructor.test.cc index 90313115d8..778ce2540d 100644 --- a/test/orange/g4org/ProtoConstructor.test.cc +++ b/test/orange/g4org/ProtoConstructor.test.cc @@ -88,7 +88,7 @@ class ProtoConstructorTest : public ::celeritas::test::Test TEST_F(ProtoConstructorTest, two_boxes) { LogicalVolume world = this->load("two-boxes.gdml"); - auto global_proto = ProtoConstructor(/* verbose = */ true)(world); + auto global_proto = ProtoConstructor(/* verbose = */ false)(world); ProtoMap protos{*global_proto}; ASSERT_EQ(1, protos.size()); { @@ -218,7 +218,7 @@ TEST_F(ProtoConstructorTest, simple_cms) // NOTE: GDML stores widths for box and cylinder Z; Geant4 uses halfwidths LogicalVolume world = this->load("simple-cms.gdml"); - auto global_proto = ProtoConstructor(/* verbose = */ true)(world); + auto global_proto = ProtoConstructor(/* verbose = */ false)(world); ProtoMap protos{*global_proto}; static std::string const expected_proto_names[] = {"world0x0"}; @@ -359,11 +359,20 @@ TEST_F(ProtoConstructorTest, znenv) auto global_proto = ProtoConstructor(/* verbose = */ false)(world); ProtoMap protos{*global_proto}; - static std::string const expected_proto_names[] - = {"World0x0", "ZNTX0x0", "ZN10x0", "ZNSL0x0", "ZNST0x0"}; + static std::string const expected_proto_names[] = { + "World0x0", + "ZNTX0x0", + "ZN10x0", + "ZNSL0x0", + "ZNST0x0", + "ZNG10x0", + "ZNG20x0", + "ZNG30x0", + "ZNG40x0", + }; EXPECT_VEC_EQ(expected_proto_names, get_proto_names(protos)); - ASSERT_EQ(5, protos.size()); + ASSERT_EQ(9, protos.size()); { SCOPED_TRACE("World"); auto u = this->build_unit(protos, UniverseId{0}); @@ -435,35 +444,40 @@ TEST_F(ProtoConstructorTest, znenv) "Plane: x=-0.05", "Plane: y=0.05", "Plane: y=0.11", - "Cyl z: r=0.01825 at x=-0.16, y=0.16", - "Cyl z: r=0.01825 at x=-0.08, y=0.08", "Plane: x=0.05", "Plane: x=0.11", - "Cyl z: r=0.01825 at x=0.16, y=0.16", - "Cyl z: r=0.01825 at x=0.08, y=0.08", "Plane: y=-0.11", "Plane: y=-0.05", - "Cyl z: r=0.01825 at x=-0.16, y=-0.16", - "Cyl z: r=0.01825 at x=-0.08, y=-0.08", - "Cyl z: r=0.01825 at x=0.16, y=-0.16", - "Cyl z: r=0.01825 at x=0.08, y=-0.08", }; static char const* const expected_volume_strings[] = { "F", - "all(all(+6, -7, +8, -9), +10)", - "-11", - "all(all(+8, -9, +12, -13), +14)", - "-15", - "all(all(+6, -7, +16, -17), +18)", - "-19", - "all(all(+12, -13, +16, -17), +20)", - "-21", + "all(+6, -7, +8, -9)", + "all(+8, -9, +10, -11)", + "all(+6, -7, +12, -13)", + "all(+10, -11, +12, -13)", }; EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); EXPECT_EQ(MaterialId{2}, u.background); } + { + SCOPED_TRACE("ZNG1"); + auto u = this->build_unit(protos, UniverseId{5}); + static char const* const expected_surface_strings[] + = {"Plane: x=-0.03", + "Plane: x=0.03", + "Plane: y=-0.03", + "Plane: y=0.03", + "Plane: z=-50", + "Plane: z=50", + "Cyl z: r=0.01825"}; + static char const* const expected_volume_strings[] = {"F", "-6", "+6"}; + + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_EQ(MaterialId{}, u.background); + } } //---------------------------------------------------------------------------// From 4e9676e42f064a40e9fdc4b0e949e128c2b99d1a Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 17 Apr 2024 17:20:50 -0400 Subject: [PATCH 07/59] Define geometry traits (#1190) * Define geometry traits * Explicitly default geoparams class --- src/geocel/GeoParamsInterface.hh | 4 ++ src/geocel/GeoTraits.hh | 53 +++++++++++++++++++++++++++ src/geocel/g4/GeantGeoTraits.hh | 48 ++++++++++++++++++++++++ src/geocel/vg/VecgeomGeoTraits.hh | 48 ++++++++++++++++++++++++ src/orange/OrangeGeoTraits.hh | 48 ++++++++++++++++++++++++ test/celeritas/AllGeoTypedTestBase.hh | 4 +- test/geocel/GenericGeoTestBase.hh | 23 +++++------- test/geocel/g4/GeantGeoTestBase.hh | 14 +------ test/geocel/vg/VecgeomTestBase.hh | 11 +----- test/orange/OrangeTestBase.hh | 12 +----- 10 files changed, 216 insertions(+), 49 deletions(-) create mode 100644 src/geocel/GeoTraits.hh create mode 100644 src/geocel/g4/GeantGeoTraits.hh create mode 100644 src/geocel/vg/VecgeomGeoTraits.hh create mode 100644 src/orange/OrangeGeoTraits.hh diff --git a/src/geocel/GeoParamsInterface.hh b/src/geocel/GeoParamsInterface.hh index 2c2bd1591c..979b693145 100644 --- a/src/geocel/GeoParamsInterface.hh +++ b/src/geocel/GeoParamsInterface.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "corecel/Macros.hh" #include "corecel/cont/Span.hh" // IWYU pragma: export #include "corecel/io/Label.hh" // IWYU pragma: export @@ -64,6 +65,9 @@ class GeoParamsInterface protected: // Protected destructor prevents deletion of pointer-to-interface ~GeoParamsInterface() = default; + + GeoParamsInterface() = default; + CELER_DEFAULT_COPY_MOVE(GeoParamsInterface); }; //---------------------------------------------------------------------------// diff --git a/src/geocel/GeoTraits.hh b/src/geocel/GeoTraits.hh new file mode 100644 index 0000000000..696e4895f2 --- /dev/null +++ b/src/geocel/GeoTraits.hh @@ -0,0 +1,53 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/GeoTraits.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Types.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class GeoParamsInterface; + +//---------------------------------------------------------------------------// +/*! + * Traits class for defining params and device data. + * \tparam G Geometry params class, e.g. VecgeomParams + * + * This traits class \em must be defined for all geometry types. The generic + * instance here is provided as a synopsis and to improve error checking. + */ +template +struct GeoTraits +{ + static_assert(std::is_base_of_v, + "G must be a geometry params, not params data"); + static_assert(std::is_void_v, "Geo traits must be specialized"); + + //! Params data used during runtime + template + using ParamsData = void; + + //! State data used during runtime + template + using StateData = void; + + //! Geometry track view + using TrackView = void; + + //! Descriptive name for the geometry + static inline char const* name = nullptr; + + //! TO BE REMOVED: "native" file extension for this geometry + static inline char const* ext = nullptr; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/g4/GeantGeoTraits.hh b/src/geocel/g4/GeantGeoTraits.hh new file mode 100644 index 0000000000..601da0c107 --- /dev/null +++ b/src/geocel/g4/GeantGeoTraits.hh @@ -0,0 +1,48 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/g4/GeantGeoTraits.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/GeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class GeantGeoParams; +class GeantGeoTrackView; +template +struct GeantGeoParamsData; +template +struct GeantGeoStateData; + +//---------------------------------------------------------------------------// +/*! + * Traits specialization for Geant4 geometry. + */ +template<> +struct GeoTraits +{ + //! Params data used during runtime + template + using ParamsData = GeantGeoParamsData; + + //! State data used during runtime + template + using StateData = GeantGeoStateData; + + //! Geometry track view + using TrackView = GeantGeoTrackView; + + //! Descriptive name for the geometry + static inline char const* name = "Geant4"; + + //! TO BE REMOVED: "native" file extension for this geometry + static inline char const* ext = ".gdml"; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/vg/VecgeomGeoTraits.hh b/src/geocel/vg/VecgeomGeoTraits.hh new file mode 100644 index 0000000000..46c933ff84 --- /dev/null +++ b/src/geocel/vg/VecgeomGeoTraits.hh @@ -0,0 +1,48 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/vg/VecgeomGeoTraits.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/GeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class VecgeomParams; +class VecgeomTrackView; +template +struct VecgeomParamsData; +template +struct VecgeomStateData; + +//---------------------------------------------------------------------------// +/*! + * Traits specialization for VecGeom geometry. + */ +template<> +struct GeoTraits +{ + //! Params data used during runtime + template + using ParamsData = VecgeomParamsData; + + //! State data used during runtime + template + using StateData = VecgeomStateData; + + //! Geometry track view + using TrackView = VecgeomTrackView; + + //! Descriptive name for the geometry + static inline char const* name = "VecGeom"; + + //! TO BE REMOVED: "native" file extension for this geometry + static inline char const* ext = ".gdml"; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/orange/OrangeGeoTraits.hh b/src/orange/OrangeGeoTraits.hh new file mode 100644 index 0000000000..9b9cddcf4c --- /dev/null +++ b/src/orange/OrangeGeoTraits.hh @@ -0,0 +1,48 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/OrangeGeoTraits.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/GeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class OrangeParams; +class OrangeTrackView; +template +struct OrangeParamsData; +template +struct OrangeStateData; + +//---------------------------------------------------------------------------// +/*! + * Traits specialization for ORANGE geometry. + */ +template<> +struct GeoTraits +{ + //! Params data used during runtime + template + using ParamsData = OrangeParamsData; + + //! State data used during runtime + template + using StateData = OrangeStateData; + + //! Geometry track view + using TrackView = OrangeTrackView; + + //! Descriptive name for the geometry + static inline char const* name = "ORANGE"; + + //! TO BE REMOVED: "native" file extension for this geometry + static inline char const* ext = ".org.json"; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/test/celeritas/AllGeoTypedTestBase.hh b/test/celeritas/AllGeoTypedTestBase.hh index 8323814616..503641e71e 100644 --- a/test/celeritas/AllGeoTypedTestBase.hh +++ b/test/celeritas/AllGeoTypedTestBase.hh @@ -60,7 +60,7 @@ class AllGeoTypedTestBase : public GenericGeoTestBase public: using SPConstGeo = typename GenericGeoTestBase::SPConstGeo; - static std::string geo_name() { return GenericGeoTraits::name; } + static std::string geo_name() { return GeoTraits::name; } SPConstGeo build_geometry() override { @@ -87,7 +87,7 @@ struct AllGeoTestingTypeNames template static std::string GetName(int) { - return GenericGeoTraits::name; + return GeoTraits::name; } }; diff --git a/test/geocel/GenericGeoTestBase.hh b/test/geocel/GenericGeoTestBase.hh index af256415e3..2cb38f32ab 100644 --- a/test/geocel/GenericGeoTestBase.hh +++ b/test/geocel/GenericGeoTestBase.hh @@ -10,6 +10,7 @@ #include #include "corecel/data/CollectionStateStore.hh" +#include "geocel/GeoTraits.hh" #include "geocel/detail/LengthUnits.hh" #include "LazyGeoManager.hh" @@ -50,33 +51,28 @@ struct GenericGeoGeantImportVolumeResult void print_expected() const; }; -//---------------------------------------------------------------------------// -//! Traits class templated on host params, to be extended by downstream files -template -struct GenericGeoTraits; - //---------------------------------------------------------------------------// /*! * Templated base class for loading geometry. * - * \tparam HP Geometry host Params class + * \tparam G Geometry host params class, e.g. OrangeParams * * \sa AllGeoTypedTestBase * * \note This class is instantiated in XTestBase.cc for geometry type X. */ -template +template class GenericGeoTestBase : virtual public Test, private LazyGeoManager { - static_assert(std::is_base_of_v); + static_assert(std::is_base_of_v); - using TraitsT = GenericGeoTraits; + using TraitsT = GeoTraits; public: //!@{ //! \name Type aliases - using SPConstGeo = std::shared_ptr; - using GeoTrackView = typename GenericGeoTraits::TrackView; + using SPConstGeo = std::shared_ptr; + using GeoTrackView = typename TraitsT::TrackView; using TrackingResult = GenericGeoTrackingResult; using GeantVolResult = GenericGeoGeantImportVolumeResult; //!@} @@ -130,8 +126,9 @@ class GenericGeoTestBase : virtual public Test, private LazyGeoManager } private: - using HostStateStore = - typename TraitsT::template StateStore; + template + using StateData = typename TraitsT::template StateData; + using HostStateStore = CollectionStateStore; SPConstGeo geo_; HostStateStore host_state_; diff --git a/test/geocel/g4/GeantGeoTestBase.hh b/test/geocel/g4/GeantGeoTestBase.hh index fe474999cb..53a980613d 100644 --- a/test/geocel/g4/GeantGeoTestBase.hh +++ b/test/geocel/g4/GeantGeoTestBase.hh @@ -11,25 +11,13 @@ #include "geocel/g4/GeantGeoData.hh" #include "geocel/g4/GeantGeoParams.hh" #include "geocel/g4/GeantGeoTrackView.hh" +#include "geocel/g4/GeantGeoTraits.hh" namespace celeritas { -//---------------------------------------------------------------------------// -class GeantGeoTrackView; - namespace test { //---------------------------------------------------------------------------// -template<> -struct GenericGeoTraits -{ - template - using StateStore = CollectionStateStore; - using TrackView = GeantGeoTrackView; - static inline char const* ext = ".gdml"; - static inline char const* name = "Geant4"; -}; - using GeantGeoTestBase = GenericGeoTestBase; //---------------------------------------------------------------------------// diff --git a/test/geocel/vg/VecgeomTestBase.hh b/test/geocel/vg/VecgeomTestBase.hh index 34b52bd735..c53d2d7bc9 100644 --- a/test/geocel/vg/VecgeomTestBase.hh +++ b/test/geocel/vg/VecgeomTestBase.hh @@ -9,6 +9,7 @@ #include "geocel/GenericGeoTestBase.hh" #include "geocel/vg/VecgeomData.hh" +#include "geocel/vg/VecgeomGeoTraits.hh" #include "geocel/vg/VecgeomParams.hh" #include "geocel/vg/VecgeomTrackView.hh" @@ -17,16 +18,6 @@ namespace celeritas namespace test { //---------------------------------------------------------------------------// -template<> -struct GenericGeoTraits -{ - template - using StateStore = CollectionStateStore; - using TrackView = VecgeomTrackView; - static inline char const* ext = ".gdml"; - static inline char const* name = "VecGeom"; -}; - using VecgeomTestBase = GenericGeoTestBase; //---------------------------------------------------------------------------// diff --git a/test/orange/OrangeTestBase.hh b/test/orange/OrangeTestBase.hh index bcdbf0c1b7..9d4d256ecf 100644 --- a/test/orange/OrangeTestBase.hh +++ b/test/orange/OrangeTestBase.hh @@ -9,6 +9,7 @@ #include "geocel/GenericGeoTestBase.hh" #include "orange/OrangeData.hh" +#include "orange/OrangeGeoTraits.hh" #include "orange/OrangeParams.hh" #include "orange/OrangeTrackView.hh" @@ -17,17 +18,6 @@ namespace celeritas namespace test { //---------------------------------------------------------------------------// -template<> -struct GenericGeoTraits -{ - template - using StateStore = CollectionStateStore; - - using TrackView = OrangeTrackView; - static inline char const* ext = ".org.json"; - static inline char const* name = "ORANGE"; -}; - using OrangeTestBase = GenericGeoTestBase; //---------------------------------------------------------------------------// From 2be4ebb86ff1b764a04d7e931f25b78d56c963ae Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 19 Apr 2024 06:47:56 -0400 Subject: [PATCH 08/59] Switch ORANGE unit tests to use GDML files (#1181) * Skip MSC in world in heuristic for better reproducibility * Activate for unit tests * Fix tests when Geant4 is unavailable * Use JSON when single precision * Restore looser bounds * Fix volume ID in step collector and enable strict testing * Activate ci version * Import existing geant4 geometry instead of reloading --- app/celer-sim/CMakeLists.txt | 6 +++ app/celer-sim/Runner.cc | 35 ++++++++++++------ app/celer-sim/simple-driver.py | 2 +- app/demo-rasterizer/CMakeLists.txt | 3 +- src/celeritas/ext/GeantImporter.hh | 3 +- src/celeritas/ext/RootImporter.hh | 3 +- src/celeritas/io/ImporterInterface.hh | 30 +++++++++++++++ test/celeritas/CMakeLists.txt | 12 +++--- test/celeritas/GlobalGeoTestBase.cc | 12 ++++-- test/celeritas/GlobalGeoTestBase.hh | 3 -- test/celeritas/geo/Geometry.test.cc | 43 ++++++++++++++-------- test/celeritas/geo/HeuristicGeoData.hh | 1 + test/celeritas/geo/HeuristicGeoExecutor.hh | 2 +- test/celeritas/geo/HeuristicGeoTestBase.cc | 13 +++++-- test/celeritas/user/MctruthTestBase.cc | 2 +- test/celeritas/user/StepCollector.test.cc | 10 ++++- test/geocel/UnitUtils.hh | 8 ++++ 17 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 src/celeritas/io/ImporterInterface.hh diff --git a/app/celer-sim/CMakeLists.txt b/app/celer-sim/CMakeLists.txt index 587094f9b6..fe1ed5f3c3 100644 --- a/app/celer-sim/CMakeLists.txt +++ b/app/celer-sim/CMakeLists.txt @@ -98,6 +98,12 @@ set_tests_properties("app/celer-sim:gpu" PROPERTIES if(CELERITAS_USE_ROOT) set(_mctruth_out "${CMAKE_CURRENT_BINARY_DIR}/mctruth_output.root") endif() +set(CELER_GEO ${CELERITAS_CORE_GEO}) +if(CELERITAS_CORE_GEO STREQUAL "ORANGE" AND NOT CELERITAS_USE_Geant4) + # Signal to the app that we have to use the JSON input + set(CELER_GEO "${CELER_CORE_GEO}-JSON") +endif() + add_test(NAME "app/celer-sim:cpu" COMMAND "${CELER_PYTHON}" "${_driver}" "${_gdml_inp}" "${_hepmc3_inp}" "${_mctruth_out}" diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 5c19626ff0..2a15a2453f 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -246,31 +246,41 @@ void Runner::setup_globals(RunnerInput const& inp) const void Runner::build_core_params(RunnerInput const& inp, SPOutputRegistry&& outreg) { + using SPImporter = std::shared_ptr; + CELER_LOG(status) << "Loading input and initializing problem data"; ScopedMem record_mem("Runner.build_core_params"); ScopedProfiling profile_this{"construct-params"}; CoreParams::Input params; - ImportData const imported = [&inp] { + + // Possible Geant4 world volume so we can reuse geometry + G4VPhysicalVolume const* g4world{nullptr}; + + // Import data and load geometry + auto import = [&inp, &g4world]() -> SPImporter { if (ends_with(inp.physics_file, ".root")) { // Load from ROOT file - return RootImporter(inp.physics_file)(); - } - std::string filename = inp.physics_file; - if (filename.empty()) - { - filename = inp.geometry_file; + return std::make_shared(inp.physics_file); } - // Load imported data directly from Geant4 - return GeantImporter(GeantSetup(filename, inp.physics_options))(); + + std::string const& filename + = !inp.physics_file.empty() ? inp.physics_file : inp.geometry_file; + + // Load Geant4 and retain to use geometry + GeantSetup setup(filename, inp.physics_options); + g4world = setup.world(); + return std::make_shared(std::move(setup)); }(); // Create action manager params.action_reg = std::make_shared(); params.output_reg = std::move(outreg); - // Load geometry - params.geometry = std::make_shared(inp.geometry_file); + // Load geometry: use existing world volume or reload from geometry file + params.geometry = g4world ? std::make_shared(g4world) + : std::make_shared(inp.geometry_file); + if (!params.geometry->supports_safety()) { CELER_LOG(warning) << "Geometry contains surfaces that are " @@ -279,6 +289,9 @@ void Runner::build_core_params(RunnerInput const& inp, "result in arbitrarily small steps"; } + // Import physics + ImportData const imported = (*import)(); + // Load materials params.material = MaterialParams::from_import(imported); diff --git a/app/celer-sim/simple-driver.py b/app/celer-sim/simple-driver.py index fb08f3b1c3..81446a7149 100755 --- a/app/celer-sim/simple-driver.py +++ b/app/celer-sim/simple-driver.py @@ -64,7 +64,7 @@ def strtobool(text): # Load directly from Geant4 rather than ROOT file physics_filename = geometry_filename -if core_geo == "orange": +if core_geo == "orange-json": print("Replacing .gdml extension since VecGeom is disabled", file=stderr) geometry_filename = re.sub(r"\.gdml$", ".org.json", geometry_filename) diff --git a/app/demo-rasterizer/CMakeLists.txt b/app/demo-rasterizer/CMakeLists.txt index 2a09bf501f..8f1fe29d27 100644 --- a/app/demo-rasterizer/CMakeLists.txt +++ b/app/demo-rasterizer/CMakeLists.txt @@ -47,7 +47,8 @@ add_test(NAME "app/demo-rasterizer" COMMAND "$" "${_driver}" "${_gdml_inp}" ) -if((CELERITAS_CORE_GEO STREQUAL "ORANGE") OR CELER_DISABLE_DEVICE) +if((CELERITAS_CORE_GEO STREQUAL "ORANGE" AND NOT CELERITAS_USE_Geant4) + OR CELER_DISABLE_DEVICE) # two-boxes.org.json isn't set up in the test directory # or device is unavailable set(_maybe_disable DISABLED true) diff --git a/src/celeritas/ext/GeantImporter.hh b/src/celeritas/ext/GeantImporter.hh index f0b14f103b..2cefc4330c 100644 --- a/src/celeritas/ext/GeantImporter.hh +++ b/src/celeritas/ext/GeantImporter.hh @@ -11,6 +11,7 @@ #include "celeritas_config.h" #include "celeritas/io/ImportData.hh" +#include "celeritas/io/ImporterInterface.hh" #include "GeantSetup.hh" @@ -63,7 +64,7 @@ struct GeantImportDataSelection ImportData data = import(); * \endcode */ -class GeantImporter +class GeantImporter final : public ImporterInterface { public: //!@{ diff --git a/src/celeritas/ext/RootImporter.hh b/src/celeritas/ext/RootImporter.hh index 6fd2cdf901..a4a90cb04c 100644 --- a/src/celeritas/ext/RootImporter.hh +++ b/src/celeritas/ext/RootImporter.hh @@ -12,6 +12,7 @@ #include "celeritas_config.h" #include "corecel/Assert.hh" #include "celeritas/io/ImportData.hh" +#include "celeritas/io/ImporterInterface.hh" #include "RootUniquePtr.hh" @@ -43,7 +44,7 @@ namespace celeritas * // And so on * \endcode */ -class RootImporter +class RootImporter final : public ImporterInterface { public: // Construct with ROOT file name diff --git a/src/celeritas/io/ImporterInterface.hh b/src/celeritas/io/ImporterInterface.hh new file mode 100644 index 0000000000..bb17d683df --- /dev/null +++ b/src/celeritas/io/ImporterInterface.hh @@ -0,0 +1,30 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/io/ImporterInterface.hh +//---------------------------------------------------------------------------// +#pragma once + +namespace celeritas +{ +//---------------------------------------------------------------------------// +struct ImportData; + +//---------------------------------------------------------------------------// +/*! + * Construct import data on demand. + */ +class ImporterInterface +{ + public: + virtual ImportData operator()() = 0; + + protected: + ImporterInterface() = default; + CELER_DEFAULT_COPY_MOVE(ImporterInterface); +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index 9bc135a072..901baa7204 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -34,7 +34,8 @@ if(NOT CELERITAS_USE_ROOT) set(_needs_root DISABLE) endif() -if(CELERITAS_CORE_GEO STREQUAL "ORANGE" AND NOT CELERITAS_USE_JSON) +if(CELERITAS_CORE_GEO STREQUAL "ORANGE" AND NOT CELERITAS_USE_JSON + AND NOT CELERITAS_USE_Geant4) set(_needs_geo DISABLE) endif() @@ -174,13 +175,14 @@ celeritas_add_test(field/MagFieldEquation.test.cc) # Geo set(CELERITASTEST_PREFIX celeritas/geo) -set(_geo_args GPU ${_needs_geo} ${_needs_double} - SOURCES geo/HeuristicGeoTestBase.cc) +set(_geo_args GPU ${_needs_geo} ${_needs_double} ${_fixme_cgs} + LINK_LIBRARIES ${_all_geo_libs} + SOURCES geo/HeuristicGeoTestBase.cc +) if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP) list(APPEND _geo_args geo/HeuristicGeoTestBase.cu) endif() -celeritas_add_test(geo/Geometry.test.cc - ${_geo_args} ${_fixme_cgs}) +celeritas_add_test(geo/Geometry.test.cc ${_geo_args}) if(NOT (CELERITAS_USE_Geant4 OR CELERITAS_USE_ROOT)) set(_needs_geant_or_root DISABLE) diff --git a/test/celeritas/GlobalGeoTestBase.cc b/test/celeritas/GlobalGeoTestBase.cc index c4f7f9979e..b8ed71878e 100644 --- a/test/celeritas/GlobalGeoTestBase.cc +++ b/test/celeritas/GlobalGeoTestBase.cc @@ -39,9 +39,15 @@ auto GlobalGeoTestBase::build_fresh_geometry(std::string_view basename) // Construct filename: // ${SOURCE}/test/celeritas/data/${basename}${fileext} - auto ext = (CELERITAS_CORE_GEO != CELERITAS_CORE_GEO_ORANGE) - ? ".gdml"sv - : ".org.json"sv; + auto ext = ".gdml"sv; + if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE + && (!CELERITAS_USE_GEANT4 + || CELERITAS_REAL_TYPE != CELERITAS_REAL_TYPE_DOUBLE)) + { + // Using ORANGE, either without Geant4 or without double-precision + // arithmetic + ext = ".org.json"sv; + } auto filename = std::string{basename} + std::string{ext}; std::string test_file = this->test_data_path("geocel", filename); return std::make_shared(test_file); diff --git a/test/celeritas/GlobalGeoTestBase.hh b/test/celeritas/GlobalGeoTestBase.hh index 10e59b9d6b..ab68e89027 100644 --- a/test/celeritas/GlobalGeoTestBase.hh +++ b/test/celeritas/GlobalGeoTestBase.hh @@ -38,9 +38,6 @@ class GlobalGeoTestBase : virtual public GlobalTestBase, // Construct a geometry that's persistent across tests SPConstGeo build_geometry() override; - // Clear the lazy geometry - static void reset_geometry(); - protected: //// LAZY GEOMETRY CONSTRUCTION AND CLEANUP //// diff --git a/test/celeritas/geo/Geometry.test.cc b/test/celeritas/geo/Geometry.test.cc index d77012fd27..7e3fc486f8 100644 --- a/test/celeritas/geo/Geometry.test.cc +++ b/test/celeritas/geo/Geometry.test.cc @@ -33,6 +33,7 @@ class TestEm3Test : public HeuristicGeoTestBase HeuristicGeoScalars result; result.lower = {-19.77, -20, -20}; result.upper = {19.43, 20, 20}; + result.world_volume = this->geometry()->find_volume("world"); return result; } @@ -75,19 +76,20 @@ auto TestEm3Test::reference_volumes() const -> SpanConstStr auto TestEm3Test::reference_avg_path() const -> SpanConstReal { - static real_type const orange_paths[] = { - 8.617, 0.1079, 0.2445, 0.1325, 0.3059, 0.1198, 0.3179, 0.1258, 0.2803, - 0.1793, 0.4181, 0.1638, 0.3871, 0.1701, 0.3309, 0.1706, 0.4996, 0.217, - 0.5396, 0.188, 0.3784, 0.1758, 0.5439, 0.3073, 0.558, 0.2541, 0.5678, - 0.2246, 0.5444, 0.3164, 0.6882, 0.3186, 0.6383, 0.2305, 0.6078, 0.2813, - 0.6735, 0.278, 0.6635, 0.2961, 0.751, 0.3612, 0.7456, 0.319, 0.7395, - 0.3557, 0.9123, 0.4129, 0.7772, 0.3561, 0.8535, 0.4207, 0.8974, 0.3674, - 0.9314, 0.4164, 0.802, 0.3904, 0.7931, 0.3276, 0.712, 0.3259, 0.7212, - 0.2625, 0.6184, 0.2654, 0.6822, 0.3073, 0.7249, 0.3216, 0.8117, 0.3101, - 0.6056, 0.2372, 0.5184, 0.1985, 0.636, 0.3183, 0.6663, 0.2881, 0.725, - 0.2805, 0.6565, 0.2635, 0.529, 0.2416, 0.5596, 0.2547, 0.5676, 0.2178, - 0.5719, 0.2943, 0.5682, 0.2161, 0.4803, 0.1719, 0.4611, 0.1928, 0.3685, - 0.1554, 0.2443}; + static real_type const orange_paths[] + = {7.504, 0.07378, 0.2057, 0.102, 0.2408, 0.1006, 0.3019, 0.1153, + 0.2812, 0.1774, 0.4032, 0.1354, 0.3163, 0.1673, 0.3465, 0.1786, + 0.4494, 0.2237, 0.5863, 0.192, 0.4027, 0.1905, 0.5949, 0.3056, + 0.5217, 0.2179, 0.5365, 0.2123, 0.5484, 0.2938, 0.634, 0.3144, + 0.6364, 0.2207, 0.5688, 0.2685, 0.6717, 0.2697, 0.6468, 0.2824, + 0.7424, 0.3395, 0.6919, 0.3018, 0.7078, 0.3441, 0.9093, 0.4125, + 0.7614, 0.334, 0.8102, 0.3901, 0.8114, 0.3377, 0.8856, 0.39, + 0.7765, 0.3847, 0.785, 0.3017, 0.6694, 0.3026, 0.7018, 0.2482, + 0.6192, 0.2405, 0.6014, 0.2733, 0.6454, 0.2804, 0.6941, 0.2608, + 0.5855, 0.2318, 0.5043, 0.1906, 0.6139, 0.3125, 0.6684, 0.3002, + 0.7295, 0.2874, 0.6328, 0.2524, 0.532, 0.2354, 0.5435, 0.2612, + 0.5484, 0.2294, 0.5671, 0.246, 0.5157, 0.1953, 0.3996, 0.1301, + 0.3726, 0.1642, 0.3317, 0.1375, 0.1909}; return make_span(orange_paths); } @@ -108,6 +110,7 @@ class SimpleCmsTest : public HeuristicGeoTestBase result.upper = {30, 30, 700}; result.log_min_step = std::log(1e-4); result.log_max_step = std::log(1e2); + result.world_volume = this->geometry()->find_volume("world"); return result; } @@ -131,7 +134,7 @@ auto SimpleCmsTest::reference_volumes() const -> SpanConstStr auto SimpleCmsTest::reference_avg_path() const -> SpanConstReal { static real_type const paths[] - = {58.02, 408.3, 274.9, 540.9, 496.3, 1134, 1694}; + = {56.38, 403, 261.3, 507.5, 467.1, 1142, 1851}; return make_span(paths); } @@ -223,12 +226,13 @@ auto CmseTest::reference_avg_path() const -> SpanConstReal TEST_F(TestEm3Test, host) { - if (not_orange_geo) + if (CELERITAS_USE_GEANT4 || CELERITAS_CORE_GEO != CELERITAS_CORE_GEO_ORANGE) { EXPECT_TRUE(this->geometry()->supports_safety()); } else { + // ORANGE from JSON file doesn't support safety EXPECT_FALSE(this->geometry()->supports_safety()); } real_type tol = not_orange_geo ? 0.35 : 1e-3; @@ -274,11 +278,18 @@ TEST_F(SimpleCmsTest, output) R"json({"bbox":[[-1000.001,-1000.001,-2000.001],[1000.001,1000.001,2000.001]],"supports_safety":true,"volumes":{"label":["vacuum_tube","si_tracker","em_calorimeter","had_calorimeter","sc_solenoid","fe_muon_chambers","world"]}})json", to_string(out)); } + else if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE + && CELERITAS_USE_GEANT4) + { + EXPECT_JSON_EQ( + R"json({"bbox":[[-1000.0,-1000.0,-2000.0],[1000.0,1000.0,2000.0]],"supports_safety":false,"surfaces":{"label":["world_box@mx","world_box@px","world_box@my","world_box@py","world_box@mz","world_box@pz","crystal_em_calorimeter@excluded.mz","crystal_em_calorimeter@excluded.pz","lhc_vacuum_tube@cz","crystal_em_calorimeter@excluded.cz","crystal_em_calorimeter@interior.cz","hadron_calorimeter@interior.cz","iron_muon_chambers@excluded.cz","iron_muon_chambers@interior.cz"]},"volumes":{"label":["[EXTERIOR]@world0x0","vacuum_tube@0x0","si_tracker@0x0","em_calorimeter@0x0","had_calorimeter@0x0","sc_solenoid@0x0","fe_muon_chambers@0x0","world@0x0"]}})json", + this->genericize_pointers(to_string(out))); + } else if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE) { EXPECT_JSON_EQ( R"json({"bbox":[[-1000.0,-1000.0,-2000.0],[1000.0,1000.0,2000.0]],"supports_safety":false,"surfaces":{"label":["world_box.mx@global","world_box.px@global","world_box.my@global","world_box.py@global","world_box.mz@global","world_box.pz@global","guide_tube.coz@global","crystal_em_calorimeter_outer.mz@global","crystal_em_calorimeter_outer.pz@global","silicon_tracker_outer.coz@global","crystal_em_calorimeter_outer.coz@global","hadron_calorimeter_outer.coz@global","superconducting_solenoid_outer.coz@global","iron_muon_chambers_outer.coz@global"]},"volumes":{"label":["[EXTERIOR]@global","vacuum_tube@global","si_tracker@global","em_calorimeter@global","had_calorimeter@global","sc_solenoid@global","fe_muon_chambers@global","world@global"]}})json", - to_string(out)); + this->genericize_pointers(to_string(out))); } } diff --git a/test/celeritas/geo/HeuristicGeoData.hh b/test/celeritas/geo/HeuristicGeoData.hh index 5598554dd2..753a8c657b 100644 --- a/test/celeritas/geo/HeuristicGeoData.hh +++ b/test/celeritas/geo/HeuristicGeoData.hh @@ -38,6 +38,7 @@ struct HeuristicGeoScalars // Set from geometry VolumeId::size_type num_volumes{}; bool ignore_zero_safety{}; + VolumeId world_volume; explicit CELER_FUNCTION operator bool() const { diff --git a/test/celeritas/geo/HeuristicGeoExecutor.hh b/test/celeritas/geo/HeuristicGeoExecutor.hh index f04a8e4080..a7cc198bd4 100644 --- a/test/celeritas/geo/HeuristicGeoExecutor.hh +++ b/test/celeritas/geo/HeuristicGeoExecutor.hh @@ -90,7 +90,7 @@ CELER_FUNCTION void HeuristicGeoExecutor::operator()(TrackSlotId tid) const // Calculate latest safety and truncate estimated step length (MSC-like) // half the time - if (!geo.is_on_boundary()) + if (!geo.is_on_boundary() && geo.volume_id() != params.s.world_volume) { real_type safety = geo.find_safety(); constexpr real_type safety_tol = 0.01; diff --git a/test/celeritas/geo/HeuristicGeoTestBase.cc b/test/celeritas/geo/HeuristicGeoTestBase.cc index 04b0d66ed2..666400871c 100644 --- a/test/celeritas/geo/HeuristicGeoTestBase.cc +++ b/test/celeritas/geo/HeuristicGeoTestBase.cc @@ -171,10 +171,15 @@ auto HeuristicGeoTestBase::get_avg_path_impl(std::vector const& path, for (auto i : range(ref_vol_labels.size())) { auto vol_id = geo.find_volume(ref_vol_labels[i]); - CELER_VALIDATE(vol_id, - << "reference volme '" << ref_vol_labels[i] - << "' is not in the geometry"); - result[i] = path[vol_id.unchecked_get()] * norm; + if (vol_id) + { + result[i] = path[vol_id.unchecked_get()] * norm; + } + else + { + ADD_FAILURE() << "reference volme '" << ref_vol_labels[i] + << "' is not in the geometry"; + } } return result; } diff --git a/test/celeritas/user/MctruthTestBase.cc b/test/celeritas/user/MctruthTestBase.cc index e5dd503e9b..2f13236605 100644 --- a/test/celeritas/user/MctruthTestBase.cc +++ b/test/celeritas/user/MctruthTestBase.cc @@ -90,7 +90,7 @@ auto MctruthTestBase::run(size_type num_tracks, size_type num_steps) { result.event.push_back(s.event); result.track.push_back(s.track); - result.step.push_back(s.step); + result.step.push_back(s.step); // Step *counter* not *length* result.volume.push_back(s.volume); for (auto pos_v : s.pos) { diff --git a/test/celeritas/user/StepCollector.test.cc b/test/celeritas/user/StepCollector.test.cc index 9b0f8afc0d..9c2b717578 100644 --- a/test/celeritas/user/StepCollector.test.cc +++ b/test/celeritas/user/StepCollector.test.cc @@ -267,7 +267,9 @@ TEST_F(TestEm3MctruthTest, four_step) EXPECT_VEC_EQ(expected_step, result.step); if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE) { - static const int expected_volume[] = {1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2}; + // 1 is gap_0 + // 101 is world + static const int expected_volume[] = {101, 1, 1, 1, 101, 1, 1, 1, 101, 1, 1, 1, 101, 1, 1, 1}; EXPECT_VEC_EQ(expected_volume, result.volume); } static const double expected_pos[] = {-22, 0, 0, -20, 0.62729376699828, @@ -302,6 +304,12 @@ TEST_F(TestEm3MctruthTest, four_step) cout << "No output saved for combination of " << test::PrintableBuildConf{} << std::endl; result.print_expected(); + + if (this->strict_testing()) + { + FAIL() << "Updated step collector results are required for CI " + "tests"; + } } } diff --git a/test/geocel/UnitUtils.hh b/test/geocel/UnitUtils.hh index a1cf61e115..1f9ac64a96 100644 --- a/test/geocel/UnitUtils.hh +++ b/test/geocel/UnitUtils.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "celeritas_config.h" #include "corecel/Types.hh" #include "corecel/cont/Array.hh" #include "geocel/detail/LengthUnits.hh" @@ -15,6 +16,13 @@ namespace celeritas { namespace test { +//---------------------------------------------------------------------------// +//! Whether the current geometry can correctly scale the input as needed +inline constexpr bool unit_scaling_enabled + = (CELERITAS_UNITS == CELERITAS_UNITS_CGS + || CELERITAS_CORE_GEO != CELERITAS_CORE_GEO_ORANGE + || CELERITAS_USE_GEANT4); + //---------------------------------------------------------------------------// //! Convert a value to centimeters from the native system constexpr inline real_type to_cm(real_type v) From 4443920c0509383ecc2fc86cade4451d03ec9ed5 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 19 Apr 2024 12:56:01 -0400 Subject: [PATCH 09/59] Fix prism orientation in Geant4 ORANGE solid converter (#1193) * Add failing test * Fix converted prism orientation --- src/orange/g4org/SolidConverter.cc | 3 ++- test/orange/g4org/SolidConverter.test.cc | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index 14467823ac..c78aaba5f4 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -474,7 +474,8 @@ auto SolidConverter::polyhedra(arg_type solid_base) -> result_type { // A solid prism double const hh = (zs[1] - zs[0]) / 2; - double const orientation = startphi.value() / params.numSide; + double const orientation + = std::fmod(params.numSide * startphi.value(), real_type{1}); if (rmin[0] != 0.0 || angle) { diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index aea078f5fe..6afaac0791 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -253,11 +253,16 @@ TEST_F(SolidConverterTest, polyhedra) static double const z[] = {-0.6, 0.6}; static double const rmin[] = {0, 0}; static double const rmax[] = {61.85, 61.85}; + // flat-top hexagon this->build_and_test( G4Polyhedra( "HGCalEEAbs", 330 * deg, 360 * deg, 6, std::size(z), z, rmin, rmax), - R"json({"_type":"shape","interior":{"_type":"prism","apothem":6.1850000000000005,"halfheight":0.06,"num_sides":6,"orientation":0.15277777777777776},"label":"HGCalEEAbs"})json", - {{6.18, 6.18, 0.05}, {0, 0, 0.06}, {7.15, 7.15, 0.05}, {6.18, 7.15, 0}}); + R"json({"_type":"shape","interior":{"_type":"prism","apothem":6.1850000000000005,"halfheight":0.06,"num_sides":6,"orientation":0.5},"label":"HGCalEEAbs"})json", + {{6.18, 6.18, 0.05}, + {0, 0, 0.06}, + {7.15, 7.15, 0.05}, + {3.0, 6.01, 0}, + {6.18, 7.15, 0}}); } TEST_F(SolidConverterTest, sphere) From 6410b4804a8eb0c0cd7116d0aaa479cf27c1322f Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Fri, 19 Apr 2024 15:09:03 -0500 Subject: [PATCH 10/59] Add optical collector for generating scintillation and Cerenkov distribution data (#1173) --- src/celeritas/CMakeLists.txt | 1 + src/celeritas/optical/OpticalCollector.cc | 53 +++ src/celeritas/optical/OpticalCollector.hh | 91 ++++ test/celeritas/CMakeLists.txt | 1 + test/celeritas/GlobalTestBase.hh | 23 ++ test/celeritas/ImportedDataTestBase.cc | 22 + test/celeritas/ImportedDataTestBase.hh | 3 + test/celeritas/LArSphereBase.hh | 41 ++ test/celeritas/MockTestBase.hh | 3 +- test/celeritas/OnlyCoreTestBase.hh | 38 ++ test/celeritas/SimpleTestBase.hh | 3 +- test/celeritas/geo/HeuristicGeoTestBase.hh | 5 +- .../optical/OpticalCollector.test.cc | 391 ++++++++++++++++++ test/geocel/data/lar-sphere.org.json | 70 ++++ test/geocel/data/lar-sphere.org.omn | 37 ++ 15 files changed, 779 insertions(+), 3 deletions(-) create mode 100644 src/celeritas/optical/OpticalCollector.cc create mode 100644 src/celeritas/optical/OpticalCollector.hh create mode 100644 test/celeritas/LArSphereBase.hh create mode 100644 test/celeritas/OnlyCoreTestBase.hh create mode 100644 test/celeritas/optical/OpticalCollector.test.cc create mode 100644 test/geocel/data/lar-sphere.org.json create mode 100644 test/geocel/data/lar-sphere.org.omn diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index f732dca5fe..1153e3e5ef 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -65,6 +65,7 @@ list(APPEND SOURCES neutron/process/NeutronElasticProcess.cc optical/CerenkovParams.cc optical/ScintillationParams.cc + optical/OpticalCollector.cc optical/OpticalPropertyParams.cc phys/CutoffParams.cc phys/ImportedModelAdapter.cc diff --git a/src/celeritas/optical/OpticalCollector.cc b/src/celeritas/optical/OpticalCollector.cc new file mode 100644 index 0000000000..75fb2de18c --- /dev/null +++ b/src/celeritas/optical/OpticalCollector.cc @@ -0,0 +1,53 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/OpticalCollector.cc +//---------------------------------------------------------------------------// +#include "OpticalCollector.hh" + +#include "celeritas/global/ActionRegistry.hh" +#include "celeritas/optical/CerenkovParams.hh" +#include "celeritas/optical/OpticalGenData.hh" +#include "celeritas/optical/OpticalPropertyParams.hh" +#include "celeritas/optical/ScintillationParams.hh" + +#include "detail/OpticalGenStorage.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct with optical params, number of streams, and action registry. + */ +OpticalCollector::OpticalCollector(Input inp) + : storage_(std::make_shared()) +{ + CELER_EXPECT(inp); + + // Create params and stream storage + HostVal host_data; + host_data.cerenkov = inp.cerenkov && inp.properties; + host_data.scintillation = static_cast(inp.scintillation); + host_data.capacity = inp.buffer_capacity; + storage_->obj = {std::move(host_data), inp.num_streams}; + storage_->size.resize(inp.num_streams, {}); + + // Action to gather pre-step data needed to generate optical distributions + gather_action_ = std::make_shared( + inp.action_registry->next_id(), storage_); + inp.action_registry->insert(gather_action_); + + // Action to generate Cerenkov and scintillation optical distributions + pregen_action_ = std::make_shared( + inp.action_registry->next_id(), + inp.properties, + inp.cerenkov, + inp.scintillation, + storage_); + inp.action_registry->insert(pregen_action_); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/optical/OpticalCollector.hh b/src/celeritas/optical/OpticalCollector.hh new file mode 100644 index 0000000000..a3974b5b1f --- /dev/null +++ b/src/celeritas/optical/OpticalCollector.hh @@ -0,0 +1,91 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/OpticalCollector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "celeritas/Types.hh" +#include "celeritas/optical/OpticalGenData.hh" + +#include "detail/PreGenAction.hh" +#include "detail/PreGenGatherAction.hh" + +namespace celeritas +{ +class ActionRegistry; +class CerenkovParams; +class OpticalPropertyParams; +class ScintillationParams; + +namespace detail +{ +struct OpticalGenStorage; +} // namespace detail + +//---------------------------------------------------------------------------// +/*! + * Generate scintillation and Cerenkov optical distribution data at each step. + * + * This builds the actions for gathering the pre-step data needed to generate + * the optical distributions and generating the optical distributions at the + * end of the step. + */ +class OpticalCollector +{ + public: + //!@{ + //! \name Type aliases + using SPConstCerenkov = std::shared_ptr; + using SPConstProperties = std::shared_ptr; + using SPConstScintillation = std::shared_ptr; + using SPGenStorage = std::shared_ptr; + //!@} + + struct Input + { + SPConstProperties properties; + SPConstCerenkov cerenkov; + SPConstScintillation scintillation; + ActionRegistry* action_registry; + size_type buffer_capacity{}; + size_type num_streams{}; + + //! True if all input is assigned and valid + explicit operator bool() const + { + return (scintillation || (cerenkov && properties)) + && action_registry && buffer_capacity > 0 && num_streams > 0; + } + }; + + public: + // Construct with optical params, number of streams, and action registry + explicit OpticalCollector(Input); + + // Default destructor and move and copy + ~OpticalCollector() = default; + CELER_DEFAULT_COPY_MOVE(OpticalCollector); + + //! Get the distribution data + SPGenStorage const& storage() const { return storage_; }; + + private: + //// TYPES //// + + using SPPreGenAction = std::shared_ptr; + using SPGatherAction = std::shared_ptr; + + //// DATA //// + + SPGenStorage storage_; + SPGatherAction gather_action_; + SPPreGenAction pregen_action_; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index 901baa7204..dfc6e51fbf 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -280,6 +280,7 @@ celeritas_add_test(io/SeltzerBergerReader.test.cc ${_needs_geant4}) # Optical set(CELERITASTEST_PREFIX celeritas/optical) celeritas_add_test(optical/Cerenkov.test.cc) +celeritas_add_test(optical/OpticalCollector.test.cc ${_needs_geant4}) celeritas_add_test(optical/Scintillation.test.cc) #-----------------------------------------------------------------------------# diff --git a/test/celeritas/GlobalTestBase.hh b/test/celeritas/GlobalTestBase.hh index bcf0516435..e425f21226 100644 --- a/test/celeritas/GlobalTestBase.hh +++ b/test/celeritas/GlobalTestBase.hh @@ -35,6 +35,10 @@ class TrackInitParams; class CoreParams; class OutputRegistry; +class CerenkovParams; +class OpticalPropertyParams; +class ScintillationParams; + namespace test { //---------------------------------------------------------------------------// @@ -67,6 +71,10 @@ class GlobalTestBase : public Test using SPActionRegistry = SP; using SPOutputRegistry = SP; + + using SPConstCerenkov = SP; + using SPConstProperties = SP; + using SPConstScintillation = SP; //!@} public: @@ -91,6 +99,9 @@ class GlobalTestBase : public Test inline SPConstTrackInit const& init(); inline SPActionRegistry const& action_reg(); inline SPConstCore const& core(); + inline SPConstCerenkov const& cerenkov(); + inline SPConstProperties const& properties(); + inline SPConstScintillation const& scintillation(); inline SPConstGeo const& geometry() const; inline SPConstMaterial const& material() const; @@ -104,6 +115,9 @@ class GlobalTestBase : public Test inline SPConstTrackInit const& init() const; inline SPActionRegistry const& action_reg() const; inline SPConstCore const& core() const; + inline SPConstCerenkov const& cerenkov() const; + inline SPConstProperties const& properties() const; + inline SPConstScintillation const& scintillation() const; //!@} //// OUTPUT //// @@ -125,6 +139,9 @@ class GlobalTestBase : public Test [[nodiscard]] virtual SPConstSim build_sim() = 0; [[nodiscard]] virtual SPConstTrackInit build_init() = 0; [[nodiscard]] virtual SPConstAction build_along_step() = 0; + [[nodiscard]] virtual SPConstCerenkov build_cerenkov() = 0; + [[nodiscard]] virtual SPConstProperties build_properties() = 0; + [[nodiscard]] virtual SPConstScintillation build_scintillation() = 0; private: SPConstRng build_rng() const; @@ -145,6 +162,9 @@ class GlobalTestBase : public Test SPConstTrackInit init_; SPConstCore core_; SPOutputRegistry output_reg_; + SPConstCerenkov cerenkov_; + SPConstProperties properties_; + SPConstScintillation scintillation_; }; //---------------------------------------------------------------------------// @@ -179,6 +199,9 @@ DEF_GTB_ACCESSORS(SPConstSim, sim) DEF_GTB_ACCESSORS(SPConstTrackInit, init) DEF_GTB_ACCESSORS(SPActionRegistry, action_reg) DEF_GTB_ACCESSORS(SPConstCore, core) +DEF_GTB_ACCESSORS(SPConstCerenkov, cerenkov) +DEF_GTB_ACCESSORS(SPConstProperties, properties) +DEF_GTB_ACCESSORS(SPConstScintillation, scintillation) #undef DEF_GTB_ACCESSORS diff --git a/test/celeritas/ImportedDataTestBase.cc b/test/celeritas/ImportedDataTestBase.cc index cc967e09ef..ae2e788cb7 100644 --- a/test/celeritas/ImportedDataTestBase.cc +++ b/test/celeritas/ImportedDataTestBase.cc @@ -10,6 +10,9 @@ #include "celeritas/geo/GeoMaterialParams.hh" #include "celeritas/io/ImportData.hh" #include "celeritas/mat/MaterialParams.hh" +#include "celeritas/optical/CerenkovParams.hh" +#include "celeritas/optical/OpticalPropertyParams.hh" +#include "celeritas/optical/ScintillationParams.hh" #include "celeritas/phys/CutoffParams.hh" #include "celeritas/phys/ParticleParams.hh" #include "celeritas/phys/PhysicsParams.hh" @@ -119,6 +122,25 @@ auto ImportedDataTestBase::build_physics() -> SPConstPhysics return std::make_shared(std::move(input)); } +//---------------------------------------------------------------------------// +auto ImportedDataTestBase::build_cerenkov() -> SPConstCerenkov +{ + return std::make_shared(this->properties()); +} + +//---------------------------------------------------------------------------// +auto ImportedDataTestBase::build_properties() -> SPConstProperties +{ + return OpticalPropertyParams::from_import(this->imported_data()); +} + +//---------------------------------------------------------------------------// +auto ImportedDataTestBase::build_scintillation() -> SPConstScintillation +{ + return ScintillationParams::from_import(this->imported_data(), + this->particle()); +} + //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas diff --git a/test/celeritas/ImportedDataTestBase.hh b/test/celeritas/ImportedDataTestBase.hh index 2cba41581a..a18c91cf44 100644 --- a/test/celeritas/ImportedDataTestBase.hh +++ b/test/celeritas/ImportedDataTestBase.hh @@ -50,6 +50,9 @@ class ImportedDataTestBase : virtual public GlobalGeoTestBase SPConstCutoff build_cutoff() override; SPConstPhysics build_physics() override; SPConstSim build_sim() override; + SPConstCerenkov build_cerenkov() override; + SPConstProperties build_properties() override; + SPConstScintillation build_scintillation() override; }; //---------------------------------------------------------------------------// diff --git a/test/celeritas/LArSphereBase.hh b/test/celeritas/LArSphereBase.hh new file mode 100644 index 0000000000..9533df0ed5 --- /dev/null +++ b/test/celeritas/LArSphereBase.hh @@ -0,0 +1,41 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/LArSphereBase.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/phys/ProcessBuilder.hh" + +#include "GeantTestBase.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +/*! + * Test harness for liquid argon sphere with optical properties. + * + * This class requires Geant4 to import the data. MSC is on by default. + */ +class LArSphereBase : virtual public GeantTestBase +{ + protected: + std::string_view geometry_basename() const override + { + return "lar-sphere"; + } + + ProcessBuilderOptions build_process_options() const override + { + ProcessBuilderOptions opts = GeantTestBase::build_process_options(); + return opts; + } +}; + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/celeritas/MockTestBase.hh b/test/celeritas/MockTestBase.hh index 46c2168da3..9d82e7610c 100644 --- a/test/celeritas/MockTestBase.hh +++ b/test/celeritas/MockTestBase.hh @@ -14,6 +14,7 @@ #include "celeritas/Types.hh" #include "GlobalGeoTestBase.hh" +#include "OnlyCoreTestBase.hh" namespace celeritas { @@ -43,7 +44,7 @@ namespace test * * Cutoff values are all zero. */ -class MockTestBase : virtual public GlobalGeoTestBase +class MockTestBase : virtual public GlobalGeoTestBase, public OnlyCoreTestBase { public: //!@{ diff --git a/test/celeritas/OnlyCoreTestBase.hh b/test/celeritas/OnlyCoreTestBase.hh new file mode 100644 index 0000000000..3e02cb1e59 --- /dev/null +++ b/test/celeritas/OnlyCoreTestBase.hh @@ -0,0 +1,38 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2022-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/OnlyCoreTestBase.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" + +#include "GlobalTestBase.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +/*! + * Mixin class providing "unreachable" implementations for optical params + * construction. + */ +class OnlyCoreTestBase : virtual public GlobalTestBase +{ + public: + SPConstCerenkov build_cerenkov() override { CELER_ASSERT_UNREACHABLE(); } + SPConstProperties build_properties() override + { + CELER_ASSERT_UNREACHABLE(); + } + SPConstScintillation build_scintillation() override + { + CELER_ASSERT_UNREACHABLE(); + } +}; +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/celeritas/SimpleTestBase.hh b/test/celeritas/SimpleTestBase.hh index c9800672ef..05589b1542 100644 --- a/test/celeritas/SimpleTestBase.hh +++ b/test/celeritas/SimpleTestBase.hh @@ -10,6 +10,7 @@ #include "corecel/Types.hh" #include "GlobalGeoTestBase.hh" +#include "OnlyCoreTestBase.hh" namespace celeritas { @@ -19,7 +20,7 @@ namespace test /*! * Compton scattering with gammas in mock aluminum in a box in hard vacuum. */ -class SimpleTestBase : virtual public GlobalGeoTestBase +class SimpleTestBase : virtual public GlobalGeoTestBase, public OnlyCoreTestBase { protected: std::string_view geometry_basename() const override { return "two-boxes"; } diff --git a/test/celeritas/geo/HeuristicGeoTestBase.hh b/test/celeritas/geo/HeuristicGeoTestBase.hh index f1febf8258..d13d5c975e 100644 --- a/test/celeritas/geo/HeuristicGeoTestBase.hh +++ b/test/celeritas/geo/HeuristicGeoTestBase.hh @@ -15,6 +15,7 @@ #include "corecel/cont/Span.hh" #include "corecel/data/Collection.hh" #include "celeritas/GlobalGeoTestBase.hh" +#include "celeritas/OnlyCoreTestBase.hh" #include "celeritas/OnlyGeoTestBase.hh" #include "HeuristicGeoData.hh" @@ -30,7 +31,9 @@ namespace test /*! * Manage a "heuristic" stepper-like test that accumulates path length. */ -class HeuristicGeoTestBase : public GlobalGeoTestBase, public OnlyGeoTestBase +class HeuristicGeoTestBase : public GlobalGeoTestBase, + public OnlyGeoTestBase, + public OnlyCoreTestBase { public: //!@{ diff --git a/test/celeritas/optical/OpticalCollector.test.cc b/test/celeritas/optical/OpticalCollector.test.cc new file mode 100644 index 0000000000..78cc9fd9aa --- /dev/null +++ b/test/celeritas/optical/OpticalCollector.test.cc @@ -0,0 +1,391 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/OpticalCollector.test.cc +//---------------------------------------------------------------------------// +#include "celeritas/optical/OpticalCollector.hh" + +#include +#include +#include + +#include "corecel/Types.hh" +#include "corecel/cont/Span.hh" +#include "corecel/data/Copier.hh" +#include "corecel/io/LogContextException.hh" +#include "geocel/UnitUtils.hh" +#include "celeritas/em/params/UrbanMscParams.hh" +#include "celeritas/global/ActionRegistry.hh" +#include "celeritas/global/Stepper.hh" +#include "celeritas/global/alongstep/AlongStepUniformMscAction.hh" +#include "celeritas/optical/OpticalCollector.hh" +#include "celeritas/optical/detail/OpticalGenStorage.hh" +#include "celeritas/phys/ParticleParams.hh" +#include "celeritas/phys/Primary.hh" +#include "celeritas/random/distribution/IsotropicDistribution.hh" + +#include "celeritas_test.hh" +#include "../LArSphereBase.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +// TEST FIXTURES +//---------------------------------------------------------------------------// + +class LArSpherePreGenTest : public LArSphereBase +{ + public: + using VecPrimary = std::vector; + + struct PreGenResult + { + size_type total_num_photons{0}; + std::vector num_photons; + std::vector charge; + }; + + struct RunResult + { + PreGenResult cerenkov; + PreGenResult scintillation; + + void print_expected() const; + }; + + public: + void SetUp() override; + + SPConstAction build_along_step() override; + + VecPrimary make_primaries(size_type count); + + template + RunResult run(size_type num_tracks, size_type num_steps); + + protected: + using SizeId = ItemId; + using BufferId = ItemId; + using BufferRange = ItemRange; + + std::shared_ptr collector_; + StreamId stream_{0}; +}; + +//---------------------------------------------------------------------------// +/*! + * Construct optical collector at setup time. + */ +void LArSpherePreGenTest::SetUp() +{ + auto& action_reg = *this->action_reg(); + + OpticalCollector::Input inp; + inp.properties = this->properties(); + inp.cerenkov = this->cerenkov(); + inp.scintillation = this->scintillation(); + inp.action_registry = &action_reg; + inp.buffer_capacity = 256; + inp.num_streams = 1; + + collector_ = std::make_shared(inp); +} + +//---------------------------------------------------------------------------// +//! Print the expected result +void LArSpherePreGenTest::RunResult::print_expected() const +{ + cout << "/*** ADD THE FOLLOWING UNIT TEST CODE ***/\n" + "EXPECT_EQ(" + << this->cerenkov.total_num_photons + << ", result.cerenkov.total_num_photons);\n" + "EXPECT_EQ(" + << this->cerenkov.num_photons.size() + << ", result.cerenkov.num_photons.size());\n" + "static size_type const expected_cerenkov_num_photons[] = " + << repr(this->cerenkov.num_photons) + << ";\n" + "EXPECT_VEC_EQ(expected_cerenkov_num_photons, " + "result.cerenkov.num_photons);\n" + "static real_type const expected_cerenkov_charge[] = " + << repr(this->cerenkov.charge) + << ";\n" + "EXPECT_VEC_EQ(expected_cerenkov_charge, " + "result.cerenkov.charge);\n" + "EXPECT_EQ(" + << this->scintillation.total_num_photons + << ", result.scintillation.total_num_photons);\n" + "EXPECT_EQ(" + << this->scintillation.num_photons.size() + << ", result.scintillation.num_photons.size());\n" + "static size_type const expected_scintillation_num_photons[] = " + << repr(this->scintillation.num_photons) + << ";\n" + "EXPECT_VEC_EQ(expected_scintillation_num_photons, " + "result.scintillation.num_photons);\n" + "static real_type const expected_scintillation_charge[] = " + << repr(this->scintillation.charge) + << ";\n" + "EXPECT_VEC_EQ(expected_scintillation_charge, " + "result.scintillation.charge);\n" + "/*** END CODE ***/\n"; +} + +//---------------------------------------------------------------------------// +/*! + * Construct along-step action. + */ +auto LArSpherePreGenTest::build_along_step() -> SPConstAction +{ + auto& action_reg = *this->action_reg(); + UniformFieldParams field_params; + field_params.field = {0, 0, 1 * units::tesla}; + auto msc = UrbanMscParams::from_import( + *this->particle(), *this->material(), this->imported_data()); + + auto result = std::make_shared( + action_reg.next_id(), field_params, nullptr, msc); + CELER_ASSERT(result); + CELER_ASSERT(result->has_msc()); + action_reg.insert(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Generate a vector of primary particles. + */ +auto LArSpherePreGenTest::make_primaries(size_type count) -> VecPrimary +{ + Primary p; + p.event_id = EventId{0}; + p.energy = units::MevEnergy{10.0}; + p.position = from_cm(Real3{0, 0, 0}); + p.time = 0; + + Array const particles = { + this->particle()->find(pdg::electron()), + this->particle()->find(pdg::positron()), + }; + CELER_ASSERT(particles[0] && particles[1]); + + std::vector result(count, p); + IsotropicDistribution<> sample_dir; + std::mt19937 rng; + + for (auto i : range(count)) + { + result[i].track_id = TrackId{i}; + result[i].direction = sample_dir(rng); + result[i].particle_id = particles[i % particles.size()]; + } + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Run a number of tracks. + */ +template +auto LArSpherePreGenTest::run(size_type num_tracks, size_type num_steps) + -> RunResult +{ + StepperInput step_inp; + step_inp.params = this->core(); + step_inp.stream_id = StreamId{0}; + step_inp.num_track_slots = num_tracks; + + Stepper step(step_inp); + LogContextException log_context{this->output_reg().get()}; + + // Initial step + auto primaries = this->make_primaries(num_tracks); + StepperResult count; + CELER_TRY_HANDLE(count = step(make_span(primaries)), log_context); + + while (count && --num_steps > 0) + { + CELER_TRY_HANDLE(count = step(), log_context); + } + + using ItemsRef + = Collection; + + auto get_result = [&](PreGenResult& result, + ItemsRef const& buffer, + size_type size) { + // Copy buffer to host + std::vector data(size); + Copier copy_data{ + make_span(data)}; + copy_data(M, buffer[BufferRange(BufferId(0), BufferId(size))]); + + std::set charge; + for (auto const& dist : data) + { + result.total_num_photons += dist.num_photons; + result.num_photons.push_back(dist.num_photons); + if (!dist) + { + continue; + } + charge.insert(dist.charge.value()); + + auto const& pre = dist.points[StepPoint::pre]; + auto const& post = dist.points[StepPoint::post]; + EXPECT_GT(pre.speed, zero_quantity()); + EXPECT_NE(post.pos, pre.pos); + EXPECT_GT(dist.step_length, 0); + EXPECT_EQ(0, dist.material.get()); + } + result.charge.insert(result.charge.end(), charge.begin(), charge.end()); + }; + + RunResult result; + CELER_ASSERT(collector_->storage()->obj.state(stream_)); + auto const& state = *(collector_->storage()->obj.state(stream_)); + auto const& sizes = collector_->storage()->size[stream_.get()]; + get_result(result.cerenkov, state.cerenkov, sizes.cerenkov); + get_result(result.scintillation, state.scintillation, sizes.scintillation); + + return result; +} + +//---------------------------------------------------------------------------// +template LArSpherePreGenTest::RunResult + LArSpherePreGenTest::run(size_type, size_type); +template LArSpherePreGenTest::RunResult + LArSpherePreGenTest::run(size_type, size_type); + +//---------------------------------------------------------------------------// +// TESTS +//---------------------------------------------------------------------------// + +TEST_F(LArSpherePreGenTest, host) +{ + auto result = this->run(4, 64); + + static real_type const expected_cerenkov_charge[] = {-1, 1}; + EXPECT_VEC_EQ(expected_cerenkov_charge, result.cerenkov.charge); + + static real_type const expected_scintillation_charge[] = {-1, 0, 1}; + EXPECT_VEC_EQ(expected_scintillation_charge, result.scintillation.charge); + + if (CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) + { + EXPECT_EQ(23492, result.cerenkov.total_num_photons); + EXPECT_EQ(48, result.cerenkov.num_photons.size()); + static size_type const expected_cerenkov_num_photons[] + = {337u, 503u, 1532u, 1485u, 788u, 610u, 1271u, 433u, 912u, 1051u, + 756u, 1124u, 796u, 854u, 446u, 420u, 582u, 648u, 704u, 825u, + 419u, 496u, 520u, 213u, 338u, 376u, 391u, 517u, 238u, 270u, + 254u, 370u, 23u, 115u, 129u, 317u, 183u, 10u, 1u, 431u, + 301u, 500u, 187u, 373u, 20u, 277u, 145u, 1u}; + EXPECT_VEC_EQ(expected_cerenkov_num_photons, + result.cerenkov.num_photons); + + EXPECT_EQ(2101748, result.scintillation.total_num_photons); + EXPECT_EQ(106, result.scintillation.num_photons.size()); + static size_type const expected_scintillation_num_photons[] = { + 27991u, 37559u, 114494u, 114637u, 58336u, 45280u, 90827u, 33901u, + 68555u, 74187u, 55095u, 83307u, 53825u, 59271u, 33295u, 30706u, + 42674u, 46522u, 48829u, 59030u, 33903u, 36690u, 38673u, 14395u, + 27169u, 29601u, 30544u, 39639u, 22299u, 23608u, 24698u, 29535u, + 18202u, 19521u, 20364u, 25475u, 10570u, 17164u, 17451u, 21131u, + 187u, 715u, 3100u, 17945u, 720u, 7048u, 13469u, 158u, + 164u, 5993u, 4529u, 167u, 614u, 167u, 450u, 3268u, + 908u, 3872u, 547u, 1188u, 1236u, 418u, 1773u, 2208u, + 5281u, 4127u, 686u, 945u, 6105u, 15114u, 180u, 2960u, + 8114u, 15870u, 1085u, 756u, 157u, 2454u, 699u, 52u, + 152u, 245u, 158u, 1486u, 6033u, 17543u, 3536u, 6809u, + 144u, 4184u, 18u, 157u, 173u, 32953u, 2615u, 618u, + 26619u, 39115u, 21551u, 30985u, 18241u, 24726u, 7999u, 20436u, + 17450u, 3420u}; + EXPECT_VEC_EQ(expected_scintillation_num_photons, + result.scintillation.num_photons); + } + else + { + EXPECT_EQ(21572, result.cerenkov.total_num_photons); + EXPECT_EQ(52, result.cerenkov.num_photons.size()); + + EXPECT_EQ(2104145, result.scintillation.total_num_photons); + EXPECT_EQ(130, result.scintillation.num_photons.size()); + } +} + +TEST_F(LArSpherePreGenTest, TEST_IF_CELER_DEVICE(device)) +{ + auto result = this->run(8, 32); + + static real_type const expected_cerenkov_charge[] = {-1, 1}; + EXPECT_VEC_EQ(expected_cerenkov_charge, result.cerenkov.charge); + + static real_type const expected_scintillation_charge[] = {-1, 0, 1}; + EXPECT_VEC_EQ(expected_scintillation_charge, result.scintillation.charge); + + if (CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) + { + EXPECT_EQ(42082, result.cerenkov.total_num_photons); + EXPECT_EQ(81, result.cerenkov.num_photons.size()); + static size_type const expected_cerenkov_num_photons[] + = {337u, 503u, 1532u, 1485u, 1376u, 1471u, 1153u, 877u, 788u, + 610u, 1271u, 433u, 1068u, 1238u, 110u, 705u, 912u, 1051u, + 756u, 1124u, 779u, 1014u, 594u, 532u, 796u, 854u, 446u, + 420u, 639u, 747u, 354u, 610u, 582u, 648u, 704u, 825u, + 475u, 579u, 827u, 478u, 419u, 496u, 520u, 213u, 107u, + 472u, 712u, 324u, 338u, 376u, 391u, 517u, 6u, 372u, + 675u, 68u, 238u, 270u, 254u, 370u, 315u, 231u, 461u, + 61u, 23u, 115u, 129u, 317u, 188u, 97u, 406u, 183u, + 22u, 268u, 10u, 128u, 26u, 153u, 1u, 105u, 2u}; + EXPECT_VEC_EQ(expected_cerenkov_num_photons, + result.cerenkov.num_photons); + + EXPECT_EQ(3759163, result.scintillation.total_num_photons); + EXPECT_EQ(193, result.scintillation.num_photons.size()); + static size_type const expected_scintillation_num_photons[] = { + 27991u, 37559u, 114494u, 114637u, 95965u, 107707u, 83200u, 66412u, + 58336u, 45280u, 90827u, 33901u, 82348u, 92196u, 8026u, 50246u, + 68555u, 74187u, 55095u, 83307u, 54711u, 71651u, 42417u, 36917u, + 53825u, 59271u, 33295u, 30706u, 45810u, 56835u, 23107u, 43619u, + 42674u, 46522u, 48829u, 59030u, 36515u, 44766u, 58969u, 34673u, + 33903u, 36690u, 38673u, 14395u, 11257u, 35520u, 54220u, 27632u, + 27169u, 29601u, 30544u, 39639u, 273u, 28493u, 47830u, 5808u, + 22299u, 23608u, 24698u, 29535u, 26582u, 22501u, 37650u, 18666u, + 18202u, 19521u, 20364u, 25475u, 21847u, 19102u, 30255u, 13766u, + 10570u, 17164u, 17451u, 21131u, 18346u, 15207u, 24321u, 715u, + 3100u, 17945u, 9017u, 19929u, 7048u, 7794u, 17347u, 6432u, + 1440u, 11157u, 2595u, 235u, 764u, 1625u, 249u, 158u, + 1814u, 150u, 2792u, 164u, 5993u, 1257u, 5934u, 129u, + 162u, 4529u, 167u, 647u, 1218u, 1983u, 584u, 4625u, + 151u, 6024u, 3268u, 165u, 1800u, 2239u, 5362u, 205u, + 5172u, 240u, 165u, 2991u, 8123u, 7040u, 668u, 947u, + 2064u, 15964u, 12994u, 4721u, 915u, 1076u, 771u, 149u, + 157u, 159u, 7225u, 12628u, 529u, 720u, 642u, 2320u, + 3945u, 8624u, 20u, 2214u, 12011u, 1827u, 144u, 5710u, + 2388u, 2504u, 2096u, 1236u, 12u, 6951u, 1226u, 260u, + 152u, 1496u, 234u, 2739u, 178u, 6033u, 150u, 162u, + 1418u, 1109u, 16u, 836u, 144u, 3751u, 702u, 18319u, + 3663u, 834u, 174u, 5538u, 20758u, 17561u, 9981u, 19808u, + 7358u, 2807u, 164u, 17548u, 4177u, 147u, 16785u, 17370u, + 4272u}; + EXPECT_VEC_EQ(expected_scintillation_num_photons, + result.scintillation.num_photons); + } + else + { + EXPECT_EQ(39194, result.cerenkov.total_num_photons); + EXPECT_EQ(81, result.cerenkov.num_photons.size()); + + EXPECT_EQ(3595786, result.scintillation.total_num_photons); + EXPECT_EQ(196, result.scintillation.num_photons.size()); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/geocel/data/lar-sphere.org.json b/test/geocel/data/lar-sphere.org.json new file mode 100644 index 0000000000..397c29bb21 --- /dev/null +++ b/test/geocel/data/lar-sphere.org.json @@ -0,0 +1,70 @@ +{ +"_format": "SCALE ORANGE", +"_version": 0, +"materials": { +"cell_to_mat": [ +-1, +1, +0 +], +"names": [ +"vacuum", +"lAr" +] +}, +"universes": [ +{ +"_type": "simple unit", +"bbox": [ +[ +-1000.000000000001, +-1000.000000000001, +-1000.000000000001 +], +[ +1000.000000000001, +1000.000000000001, +1000.000000000001 +] +], +"cell_names": [ +"[EXTERIOR]", +"sphere", +"world" +], +"cells": [ +{ +"faces": [ 0 ], +"logic": "0", +"num_intersections": 2, +"zorder": 2 +}, +{ +"faces": [ 1 ], +"logic": "0 ~", +"num_intersections": 2, +"zorder": 2 +}, +{ +"faces": [ 0, 1 ], +"logic": "0 ~ 1 &", +"num_intersections": 4, +"zorder": 2 +} +], +"md": { +"name": "global", +"provenance": "" +}, +"surface_names": [ +"world_sphere.s", +"sphere.s" +], +"surfaces": { +"data": [ 1000000.0, 10000.0 ], +"sizes": [ 1, 1 ], +"types": [ "sc", "sc" ] +} +} +] +} diff --git a/test/geocel/data/lar-sphere.org.omn b/test/geocel/data/lar-sphere.org.omn new file mode 100644 index 0000000000..5db62fe4af --- /dev/null +++ b/test/geocel/data/lar-sphere.org.omn @@ -0,0 +1,37 @@ +!############################################################################## +! File : lar-sphere.org.omn +! +! One steel sphere. +! Regenerate the JSON file with `orange2celeritas` from SCALE +!############################################################################## + +[GEOMETRY] +global "global" +comp : matid + "vacuum" 0 + "lAr" 1 + +[UNIVERSE=general global] +interior "world" + +!############################################################################## +! SHAPES ("solids") +!############################################################################## + +[UNIVERSE][SHAPE=sphere sphere] +radius 100 + +[UNIVERSE][SHAPE=sphere world_sphere] +radius 1000 + +!############################################################################## +! CELLS ("volumes") +!############################################################################## + +[UNIVERSE][CELL sphere] +comp "lAr" +shapes sphere + +[UNIVERSE][CELL world] +comp "vacuum" +shapes world_sphere ~sphere From 57ef806c1424e11f03299f0443fe60396e28e77d Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Sun, 21 Apr 2024 07:58:17 -0400 Subject: [PATCH 11/59] Update esseivaj user presets (#1195) --- scripts/cmake-presets/esseivaj.json | 62 +++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/scripts/cmake-presets/esseivaj.json b/scripts/cmake-presets/esseivaj.json index ab9272982b..0a594b0164 100644 --- a/scripts/cmake-presets/esseivaj.json +++ b/scripts/cmake-presets/esseivaj.json @@ -3,11 +3,11 @@ "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, "configurePresets": [ { - "name": "base", + "name": ".base", + "hidden": true, "inherits": ["default", ".debug"], - "displayName": "esseivaj default options", - "binaryDir": "${sourceDir}/build-${presetName}", "generator": "Ninja", + "binaryDir": "${sourceDir}/build-${presetName}", "cacheVariables": { "CELERITAS_BUILD_DOCS": {"type": "BOOL", "value": "OFF"}, "CELERITAS_BUILD_DEMOS": {"type": "BOOL", "value": "ON"}, @@ -21,8 +21,46 @@ "CELERITAS_USE_SWIG": {"type": "BOOL", "value": "OFF"}, "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "ON"}, "CMAKE_CXX_STANDARD": {"type": "STRING", "value": "17"}, - "CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wno-error=deprecated -pedantic -fdiagnostics-color=always" + "CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wno-error=deprecated -pedantic -fdiagnostics-color=always", + "CMAKE_EXPORT_COMPILE_COMMANDS": {"type": "BOOL", "value": "ON"} + } + }, + { + "name": ".no-vg", + "hidden": true, + "cacheVariables": { + "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"} } + }, + { + "name": "base", + "displayName": "Vecgeom debug", + "inherits": [".base"] + }, + { + "name": "base-novg", + "displayName": "ORANGE debug", + "inherits": [".no-vg", ".base"] + }, + { + "name": "reldeb", + "displayName": "Vecgeom release debug", + "inherits": [".reldeb", ".base"] + }, + { + "name": "reldeb-novg", + "displayName": "ORANGE release debug", + "inherits": [".no-vg", ".reldeb", ".base"] + }, + { + "name": "ndebug", + "displayName": "Vecgeom release", + "inherits": [".ndebug", ".base"] + }, + { + "name": "ndebug-novg", + "displayName": "ORANGE release", + "inherits": [".no-vg", ".ndebug", ".base"] } ], "buildPresets": [ @@ -31,14 +69,24 @@ "configurePreset": "base", "jobs": 16, "nativeToolOptions": ["-k0"] - } + }, + {"name": "base-novg", "configurePreset": "base-novg", "inherits": "base"}, + {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"}, + {"name": "ndebug-novg", "configurePreset": "ndebug-novg", "inherits": "base"}, + {"name": "reldeb", "configurePreset": "reldeb", "inherits": "base"}, + {"name": "reldeb-novg", "configurePreset": "reldeb-novg", "inherits": "base"} ], "testPresets": [ { "name": "base", "configurePreset": "base", "output": {"outputOnFailure": true}, - "execution": {"noTestsAction": "error", "stopOnFailure": false} - } + "execution": {"noTestsAction": "error", "stopOnFailure": false, "jobs": 8} + }, + {"name": "base-novg", "configurePreset": "base-novg", "inherits": "base"}, + {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"}, + {"name": "ndebug-novg", "configurePreset": "ndebug-novg", "inherits": "base"}, + {"name": "reldeb", "configurePreset": "reldeb", "inherits": "base"}, + {"name": "reldeb-novg", "configurePreset": "reldeb-novg", "inherits": "base"} ] } From 384a86df5cb7b7a14f8acca5f5161ce09b52fc18 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Mon, 22 Apr 2024 14:01:29 -0400 Subject: [PATCH 12/59] Parse log levels from Geant4 scoped logger (#1189) * Parse warning/error message levels from Geant4 * Add tests for scoped logger * Add additional examples and checks --- src/corecel/io/StringUtils.hh | 2 +- src/geocel/ScopedGeantLogger.cc | 61 ++++++++++++++++- test/geocel/CMakeLists.txt | 13 ++-- test/geocel/ScopedGeantLogger.test.cc | 98 +++++++++++++++++++++++++++ 4 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 test/geocel/ScopedGeantLogger.test.cc diff --git a/src/corecel/io/StringUtils.hh b/src/corecel/io/StringUtils.hh index 3591473f2e..2a97ce1723 100644 --- a/src/corecel/io/StringUtils.hh +++ b/src/corecel/io/StringUtils.hh @@ -27,7 +27,7 @@ bool is_ignored_trailing(unsigned char c); //---------------------------------------------------------------------------// // Return a string view with leading and trailing whitespace removed -std::string_view trim(std::string_view input); +[[nodiscard]] std::string_view trim(std::string_view input); //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/geocel/ScopedGeantLogger.cc b/src/geocel/ScopedGeantLogger.cc index acb67f5fc1..88f2010ad4 100644 --- a/src/geocel/ScopedGeantLogger.cc +++ b/src/geocel/ScopedGeantLogger.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -32,14 +33,72 @@ namespace celeritas { namespace { +//---------------------------------------------------------------------------// +//! Get a string view matched by a regular expression +template +std::string_view to_string_view(std::sub_match const& cs) +{ + if (!cs.matched) + { + return {}; + } + return {&(*cs.first), static_cast(cs.length())}; +} + //---------------------------------------------------------------------------// /*! * Log the actual message. */ G4int log_impl(G4String const& str, LogLevel level) { + static std::regex const err_warn_regex{ + R"regex(^\W*(\w+)?\s*(warning|error|!+|\*+)\W+)regex", + std::regex::icase}; + + static std::regex const info_regex{R"regex(^(\w+):\s+)regex"}; + + std::smatch m; + std::string_view msg; + std::string_view source{"Geant4"}; + if (std::regex_search(str, m, err_warn_regex)) + { + CELER_ASSERT(m.size() == 3); + if (m[1].matched) + { + // Warning is coming from somewhere in particular + source = to_string_view(m[1]); + } + + // Strip the beginning text from the err/warning + msg = to_string_view(m.suffix()); + // Update the warning level + auto first_char = std::tolower(static_cast(*m[2].first)); + if (first_char == 'w' || first_char == '*') + { + level = LogLevel::warning; + } + else if (first_char == 'e' || first_char == '!') + { + level = LogLevel::error; + } + else + { + CELER_ASSERT_UNREACHABLE(); + } + } + else if (std::regex_search(str, m, info_regex)) + { + CELER_ASSERT(m.size() == 2); + source = to_string_view(m[1]); + msg = to_string_view(m.suffix()); + } + else + { + msg = str; + } + // Output with dummy file/line - ::celeritas::world_logger()({"Geant4", 0}, level) << trim(str); + ::celeritas::world_logger()({source, 0}, level) << trim(msg); // 0 for success, -1 for failure return 0; diff --git a/test/geocel/CMakeLists.txt b/test/geocel/CMakeLists.txt index aff7364ca1..708daa0a70 100644 --- a/test/geocel/CMakeLists.txt +++ b/test/geocel/CMakeLists.txt @@ -8,6 +8,7 @@ if(NOT CELERITAS_USE_Geant4) set(_needs_geant4 DISABLE) else() celeritas_get_g4libs(_g4_geo_libs geometry) + celeritas_get_g4libs(_g4_global_libs global) endif() if(CELERITAS_UNITS STREQUAL "CGS") @@ -102,13 +103,13 @@ if(CELERITAS_USE_VecGeom) ) endif() +if(CELERITAS_USE_Geant4) + celeritas_add_test(ScopedGeantLogger.test.cc LINK_LIBRARIES ${_g4_global_libs}) +endif() + if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") - celeritas_add_test(g4/GeantGeo.test.cc - LINK_LIBRARIES ${_g4_geo_libs} - ) - celeritas_add_test(GeantGeoUtils.test.cc - LINK_LIBRARIES ${_g4_geo_libs} - ) + celeritas_add_test(g4/GeantGeo.test.cc LINK_LIBRARIES ${_g4_geo_libs}) + celeritas_add_test(GeantGeoUtils.test.cc LINK_LIBRARIES ${_g4_geo_libs}) endif() #-----------------------------------------------------------------------------# diff --git a/test/geocel/ScopedGeantLogger.test.cc b/test/geocel/ScopedGeantLogger.test.cc new file mode 100644 index 0000000000..44249b591c --- /dev/null +++ b/test/geocel/ScopedGeantLogger.test.cc @@ -0,0 +1,98 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/ScopedGeantLogger.test.cc +//---------------------------------------------------------------------------// +#include "geocel/ScopedGeantLogger.hh" + +#include + +#include "corecel/ScopedLogStorer.hh" +#include "corecel/io/Logger.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class ScopedGeantLoggerTest : public ::celeritas::test::Test +{ + protected: + void SetUp() override {} +}; + +TEST_F(ScopedGeantLoggerTest, host) +{ + ScopedGeantLogger scoped_g4; + G4cout << "This is not be captured by scoped logger" << endl; + + ScopedLogStorer scoped_log_{&celeritas::world_logger(), LogLevel::debug}; + G4cout << "Standard output" << endl; + G4cerr << "Standard err" << endl; + G4cerr << "WARNING - nub nub" << endl; + G4cout << "warning: from cout" << endl; + G4cerr << "ERROR - derpaderp" << endl; + G4cout << "G4Material warning: things are bad" << endl; + G4cerr << "!!! Csv file name not defined." << endl; + G4cerr << "ERROR : smish" << endl; + G4cerr << "*** oh boy ***" << endl; + G4cerr << "Error! -- 123 HCIO assignment failed" << endl; + G4cout << "G4GDML: doing things" << endl; + + static char const* const expected_log_messages[] = { + "Standard output", + "Standard err", + "nub nub", + "from cout", + "derpaderp", + "things are bad", + "Csv file name not defined.", + "smish", + "oh boy ***", + "123 HCIO assignment failed", + "doing things", + }; + EXPECT_VEC_EQ(expected_log_messages, scoped_log_.messages()); + static char const* const expected_log_levels[] = { + "diagnostic", + "info", + "warning", + "warning", + "error", + "warning", + "error", + "error", + "warning", + "error", + "diagnostic", + }; + EXPECT_VEC_EQ(expected_log_levels, scoped_log_.levels()); +} + +TEST_F(ScopedGeantLoggerTest, nesting) +{ + ScopedGeantLogger scoped_g4; + { + ScopedGeantLogger scoped_g4; + { + ScopedGeantLogger scoped_g4; + ScopedLogStorer scoped_log_{&celeritas::world_logger(), + LogLevel::debug}; + G4cout << "This should still work" << endl; + static char const* const expected_log_messages[] + = {"This should still work"}; + EXPECT_VEC_EQ(expected_log_messages, scoped_log_.messages()); + static char const* const expected_log_levels[] = {"diagnostic"}; + EXPECT_VEC_EQ(expected_log_levels, scoped_log_.levels()); + } + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas From 390ed0e440bce595a6a331b2caf173b9d303e4bf Mon Sep 17 00:00:00 2001 From: Soon Yung Jun Date: Tue, 23 Apr 2024 08:01:31 -0500 Subject: [PATCH 13/59] Add neutron inelastic process (#1187) * Add the first skeleton of neutron inelastic model and process, and associated cross section data * Add neutron inelastic model and process * Add ChannelId for neutron codes * Add NeutronXsType * Add missing braces for around initialization of Real3 input data * Change StepanovParameters::xs_o to xs_zero * Change execute with CELER_NOT_IMPLEMENTED * Use GenericGridBuilder * Restructure the NN cross section calculation below 10 MeV * Add an assertion for the positive slope --- src/celeritas/CMakeLists.txt | 2 + src/celeritas/Types.hh | 3 + src/celeritas/io/NeutronXsReader.cc | 29 ++- src/celeritas/io/NeutronXsReader.hh | 31 ++- .../neutron/data/NeutronInelasticData.hh | 119 ++++++++++ .../executor/NeutronInelasticExecutor.hh | 63 +++++ .../interactor/NeutronInelasticInteractor.hh | 77 +++++++ .../neutron/model/NeutronInelasticModel.cc | 215 ++++++++++++++++++ .../neutron/model/NeutronInelasticModel.cu | 26 +++ .../neutron/model/NeutronInelasticModel.hh | 102 +++++++++ .../process/NeutronInelasticProcess.cc | 70 ++++++ .../process/NeutronInelasticProcess.hh | 63 +++++ .../xs/NeutronInelasticMicroXsCalculator.hh | 84 +++++++ .../neutron/xs/NucleonNucleonXsCalculator.hh | 121 ++++++++++ src/celeritas/phys/ProcessBuilder.cc | 2 +- test/celeritas/CMakeLists.txt | 1 + test/celeritas/data/inel2 | 1 + test/celeritas/data/inel29 | 1 + test/celeritas/neutron/NeutronElastic.test.cc | 2 +- .../neutron/NeutronInelastic.test.cc | 179 +++++++++++++++ 20 files changed, 1178 insertions(+), 13 deletions(-) create mode 100644 src/celeritas/neutron/data/NeutronInelasticData.hh create mode 100644 src/celeritas/neutron/executor/NeutronInelasticExecutor.hh create mode 100644 src/celeritas/neutron/interactor/NeutronInelasticInteractor.hh create mode 100644 src/celeritas/neutron/model/NeutronInelasticModel.cc create mode 100644 src/celeritas/neutron/model/NeutronInelasticModel.cu create mode 100644 src/celeritas/neutron/model/NeutronInelasticModel.hh create mode 100644 src/celeritas/neutron/process/NeutronInelasticProcess.cc create mode 100644 src/celeritas/neutron/process/NeutronInelasticProcess.hh create mode 100644 src/celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh create mode 100644 src/celeritas/neutron/xs/NucleonNucleonXsCalculator.hh create mode 100644 test/celeritas/data/inel2 create mode 100644 test/celeritas/data/inel29 create mode 100644 test/celeritas/neutron/NeutronInelastic.test.cc diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 1153e3e5ef..15c380e6d1 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -63,6 +63,7 @@ list(APPEND SOURCES mat/MaterialParamsOutput.cc mat/detail/Utils.cc neutron/process/NeutronElasticProcess.cc + neutron/process/NeutronInelasticProcess.cc optical/CerenkovParams.cc optical/ScintillationParams.cc optical/OpticalCollector.cc @@ -256,6 +257,7 @@ celeritas_polysource(global/alongstep/AlongStepNeutralAction) celeritas_polysource(global/alongstep/AlongStepUniformMscAction) celeritas_polysource(global/alongstep/AlongStepRZMapFieldMscAction) celeritas_polysource(neutron/model/ChipsNeutronElasticModel) +celeritas_polysource(neutron/model/NeutronInelasticModel) celeritas_polysource(optical/detail/PreGenAction) celeritas_polysource(optical/detail/PreGenGatherAction) celeritas_polysource(phys/detail/DiscreteSelectAction) diff --git a/src/celeritas/Types.hh b/src/celeritas/Types.hh index 4858c6a4da..4b8015232d 100644 --- a/src/celeritas/Types.hh +++ b/src/celeritas/Types.hh @@ -72,6 +72,9 @@ using ParticleModelId = OpaqueId; //! Opaque index of electron subshell using SubshellId = OpaqueId; +//! Opaque index of particle-nucleon cascade channel +using ChannelId = OpaqueId; + //---------------------------------------------------------------------------// // ENUMERATIONS //---------------------------------------------------------------------------// diff --git a/src/celeritas/io/NeutronXsReader.cc b/src/celeritas/io/NeutronXsReader.cc index abb83b16dd..a9607c5512 100644 --- a/src/celeritas/io/NeutronXsReader.cc +++ b/src/celeritas/io/NeutronXsReader.cc @@ -12,8 +12,8 @@ #include "corecel/Assert.hh" #include "corecel/Types.hh" +#include "corecel/io/EnumStringMapper.hh" #include "corecel/io/Logger.hh" -#include "corecel/math/Algorithms.hh" #include "corecel/sys/Environment.hh" #include "celeritas/Quantities.hh" #include "celeritas/Units.hh" @@ -26,12 +26,12 @@ namespace celeritas * Construct the reader using the G4PARTICLEXSDATA environment variable to get * the path to the data. */ -NeutronXsReader::NeutronXsReader() +NeutronXsReader::NeutronXsReader(NeutronXsType type) : type_(type) { std::string const& dir = celeritas::getenv("G4PARTICLEXSDATA"); CELER_VALIDATE(!dir.empty(), << "environment variable G4PARTICLEXSDATA is not defined " - "(needed to locate neutron elastic cross section data)"); + "(needed to locate neutron cross section data)"); path_ = dir + "/neutron"; } @@ -39,7 +39,8 @@ NeutronXsReader::NeutronXsReader() /*! * Construct the reader with the path to the directory containing the data. */ -NeutronXsReader::NeutronXsReader(char const* path) : path_(path) +NeutronXsReader::NeutronXsReader(NeutronXsType type, char const* path) + : type_(type), path_(path) { CELER_EXPECT(!path_.empty()); if (path_.back() == '/') @@ -58,13 +59,14 @@ NeutronXsReader::operator()(AtomicNumber atomic_number) const CELER_EXPECT(atomic_number); std::string z_str = std::to_string(atomic_number.unchecked_get()); - CELER_LOG(debug) << "Reading neutron elastic xs data for Z=" << z_str; + CELER_LOG(debug) << "Reading neutron xs data for " << to_cstring(type_) + << " Z=" << z_str; result_type result; // Read neutron elastic cross section data for the given atomic_number { - std::string filename = path_ + "/el" + z_str; + std::string filename = path_ + "/" + to_cstring(type_) + z_str; std::ifstream infile(filename); CELER_VALIDATE(infile, << "failed to open '" << filename @@ -99,5 +101,20 @@ NeutronXsReader::operator()(AtomicNumber atomic_number) const return result; } +//---------------------------------------------------------------------------// +// FREE FUNCTIONS +//---------------------------------------------------------------------------// + +//---------------------------------------------------------------------------// +/*! + * Get the string value for a neutron cross section data type. + */ +char const* to_cstring(NeutronXsType value) +{ + static EnumStringMapper const to_cstring_impl{ + "cap", "el", "inel"}; + return to_cstring_impl(value); +} + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/io/NeutronXsReader.hh b/src/celeritas/io/NeutronXsReader.hh index 11b7db8760..2157690501 100644 --- a/src/celeritas/io/NeutronXsReader.hh +++ b/src/celeritas/io/NeutronXsReader.hh @@ -21,7 +21,20 @@ namespace celeritas { //---------------------------------------------------------------------------// /*! - * Load the neutron elastic cross section (G4PARTICLEXSDATA/neutron/elZ) data. + * Types of microscopic cross sections in G4PARTICLEXSDATA/neutron data. + */ +enum class NeutronXsType +{ + cap, //!< Capture cross section + el, //!< Elastic cross section + inel, //!< Inelastic cross section + size_ +}; + +//---------------------------------------------------------------------------// +/*! + * Load the neutron cross section (G4PARTICLEXSDATA/neutron) data by the + * interaction type (capture, elastic, and inelastic). */ class NeutronXsReader { @@ -35,18 +48,26 @@ class NeutronXsReader public: // Construct the reader and locate the data using the environment variable - NeutronXsReader(); + explicit NeutronXsReader(NeutronXsType type); - // Construct the reader from the path to the data directory - explicit NeutronXsReader(char const* path); + // Construct the reader from the path to the data directory and the type + NeutronXsReader(NeutronXsType type, char const* path); // Read the data for the given element result_type operator()(AtomicNumber atomic_number) const; private: - // Directory containing the neutron elastic cross section data + // Type and directory containing the neutron elastic cross section data + NeutronXsType type_; std::string path_; }; +//---------------------------------------------------------------------------// +// FREE FUNCTIONS +//---------------------------------------------------------------------------// + +// Get the string value for a neutron cross section type +char const* to_cstring(NeutronXsType value); + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/neutron/data/NeutronInelasticData.hh b/src/celeritas/neutron/data/NeutronInelasticData.hh new file mode 100644 index 0000000000..45aedb11a8 --- /dev/null +++ b/src/celeritas/neutron/data/NeutronInelasticData.hh @@ -0,0 +1,119 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/data/NeutronInelasticData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "corecel/data/Collection.hh" +#include "corecel/math/Quantity.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/Types.hh" +#include "celeritas/grid/GenericGridData.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Scalar data for neutron-nucleus inelastic interactions. + */ +struct NeutronInelasticScalars +{ + // Action and particle IDs + ActionId action_id; + ParticleId neutron_id; + + // Particle mass * c^2 [MeV] + units::MevMass neutron_mass; + + //! Number of nucleon-nucleon channels + static CELER_CONSTEXPR_FUNCTION size_type num_channels() { return 3; } + + //! Model's maximum energy limit [MeV] + static CELER_CONSTEXPR_FUNCTION units::MevEnergy max_valid_energy() + { + // Below the pion production threshold + return units::MevEnergy{320}; + } + + //! Whether data are assigned + explicit CELER_FUNCTION operator bool() const + { + return action_id && neutron_id && neutron_mass > zero_quantity(); + } +}; + +//---------------------------------------------------------------------------// +/*! + * Parameters of Stepanov's function to fit nucleon-nucleon cross sections + * below 10 MeV. + */ +struct StepanovParameters +{ + real_type xs_zero; //!< nucleon-nucleon cross section at the zero energy + real_type slope; //!< parameter used for the low energy threshold + Real3 coeffs; //!< coefficients of a second order Stepanov's function +}; + +//---------------------------------------------------------------------------// +/*! + * Device data for creating an interactor. + */ +template +struct NeutronInelasticData +{ + template + using Items = Collection; + template + using ElementItems = Collection; + template + using ChannelItems = Collection; + + //// MEMBER DATA //// + + // Scalar data + NeutronInelasticScalars scalars; + + // Microscopic (element) cross section data (G4PARTICLEXS/neutron/inelZ) + ElementItems micro_xs; + + // Tabulated nucleon-nucleon cross section data + ChannelItems nucleon_xs; + + // Parameters of necleon-nucleon cross sections below 10 MeV + ChannelItems xs_params; + + // Backend data + Items reals; + + //! Whether the data are assigned + explicit CELER_FUNCTION operator bool() const + { + return scalars && !micro_xs.empty() && !nucleon_xs.empty() + && !xs_params.empty() && !reals.empty(); + } + + //! Assign from another set of data + template + NeutronInelasticData& operator=(NeutronInelasticData const& other) + { + CELER_EXPECT(other); + scalars = other.scalars; + micro_xs = other.micro_xs; + nucleon_xs = other.nucleon_xs; + xs_params = other.xs_params; + reals = other.reals; + return *this; + } +}; + +using NeutronInelasticHostRef = HostCRef; +using NeutronInelasticDeviceRef = DeviceCRef; +using NeutronInelasticRef = NativeCRef; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/executor/NeutronInelasticExecutor.hh b/src/celeritas/neutron/executor/NeutronInelasticExecutor.hh new file mode 100644 index 0000000000..1cc1412b47 --- /dev/null +++ b/src/celeritas/neutron/executor/NeutronInelasticExecutor.hh @@ -0,0 +1,63 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/executor/NeutronInelasticExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "celeritas/global/CoreTrackView.hh" +#include "celeritas/mat/ElementSelector.hh" +#include "celeritas/mat/ElementView.hh" +#include "celeritas/neutron/data/NeutronInelasticData.hh" +#include "celeritas/neutron/interactor/NeutronInelasticInteractor.hh" +#include "celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh" +#include "celeritas/phys/Interaction.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +struct NeutronInelasticExecutor +{ + inline CELER_FUNCTION Interaction + operator()(celeritas::CoreTrackView const& track); + + NeutronInelasticRef params; +}; + +//---------------------------------------------------------------------------// +/*! + * Apply the NeutronInelasticInteractor to the current track. + */ +CELER_FUNCTION Interaction +NeutronInelasticExecutor::operator()(CoreTrackView const& track) +{ + auto particle = track.make_particle_view(); + auto rng = track.make_rng_engine(); + + // Select a target element + auto material = track.make_material_view().make_material_view(); + auto elcomp_id = track.make_physics_step_view().element(); + if (!elcomp_id) + { + // Sample an element (based on element cross sections on the fly) + ElementSelector select_el( + material, + NeutronInelasticMicroXsCalculator{params, particle.energy()}, + track.make_material_view().element_scratch()); + elcomp_id = select_el(rng); + CELER_ASSERT(elcomp_id); + track.make_physics_step_view().element(elcomp_id); + } + + // Construct the interactor + NeutronInelasticInteractor interact(params, particle); + + // Execute the interactor + return interact(rng); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/interactor/NeutronInelasticInteractor.hh b/src/celeritas/neutron/interactor/NeutronInelasticInteractor.hh new file mode 100644 index 0000000000..0af0564dff --- /dev/null +++ b/src/celeritas/neutron/interactor/NeutronInelasticInteractor.hh @@ -0,0 +1,77 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/interactor/NeutronInelasticInteractor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "celeritas/mat/IsotopeView.hh" +#include "celeritas/neutron/data/NeutronInelasticData.hh" +#include "celeritas/phys/Interaction.hh" +#include "celeritas/phys/ParticleTrackView.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Perform neutron inelastic interaction based on the Bertini cascade model. + * + * \note This performs the sampling procedure as in G4CascadeInterface, as + * documented in section 24 of the Geant4 Physics Reference (release 11.2). + */ +class NeutronInelasticInteractor +{ + public: + //!@{ + //! \name Type aliases + using Energy = units::MevEnergy; + using Mass = units::MevMass; + using Momentum = units::MevMomentum; + //!@} + + public: + // Construct from shared and state data + inline CELER_FUNCTION + NeutronInelasticInteractor(NeutronInelasticRef const& shared, + ParticleTrackView const& particle); + + // Sample an interaction with the given RNG + template + inline CELER_FUNCTION Interaction operator()(Engine& rng); + + private: + //// DATA //// + + // Constant shared data + NeutronInelasticRef const& shared_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with shared and state data, and a target nucleus. + */ +CELER_FUNCTION +NeutronInelasticInteractor::NeutronInelasticInteractor( + NeutronInelasticRef const& shared, ParticleTrackView const& particle) + : shared_(shared) +{ + CELER_EXPECT(particle.particle_id() == shared_.scalars.neutron_id); +} +//---------------------------------------------------------------------------// +/*! + * Sample the final state of the neutron-nucleus inelastic interaction. + */ +template +CELER_FUNCTION Interaction NeutronInelasticInteractor::operator()(Engine&) +{ + CELER_NOT_IMPLEMENTED("Neutron inelastic interaction"); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/model/NeutronInelasticModel.cc b/src/celeritas/neutron/model/NeutronInelasticModel.cc new file mode 100644 index 0000000000..bbe2b1fc0a --- /dev/null +++ b/src/celeritas/neutron/model/NeutronInelasticModel.cc @@ -0,0 +1,215 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/model/NeutronInelasticModel.cc +//---------------------------------------------------------------------------// +#include "NeutronInelasticModel.hh" + +#include "corecel/math/ArrayOperators.hh" +#include "corecel/math/Quantity.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/grid/GenericGridBuilder.hh" +#include "celeritas/io/ImportPhysicsVector.hh" +#include "celeritas/mat/MaterialParams.hh" +#include "celeritas/phys/InteractionApplier.hh" +#include "celeritas/phys/PDGNumber.hh" +#include "celeritas/phys/ParticleParams.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct from model ID and other necessary data. + */ +NeutronInelasticModel::NeutronInelasticModel(ActionId id, + ParticleParams const& particles, + MaterialParams const& materials, + ReadData load_data) +{ + CELER_EXPECT(id); + CELER_EXPECT(load_data); + + HostVal data; + + // Save IDs + data.scalars.action_id = id; + data.scalars.neutron_id = particles.find(pdg::neutron()); + + CELER_VALIDATE(data.scalars.neutron_id, + << "missing neutron particles (required for " + << this->description() << ")"); + + // Save particle properties + data.scalars.neutron_mass = particles.get(data.scalars.neutron_id).mass(); + + // Load neutron inelastic cross section data + CollectionBuilder micro_xs{&data.micro_xs}; + GenericGridBuilder build_grid{&data.reals}; + for (auto el_id : range(ElementId{materials.num_elements()})) + { + AtomicNumber z = materials.get(el_id).atomic_number(); + micro_xs.push_back(build_grid(load_data(z))); + } + CELER_ASSERT(data.micro_xs.size() == materials.num_elements()); + + // Build nucleon-nucleon cross section data + size_type num_channels = data.scalars.num_channels(); + make_builder(&data.nucleon_xs).reserve(num_channels); + auto xs_params = make_builder(&data.xs_params); + xs_params.reserve(num_channels); + + auto bins = this->get_channel_bins(); + for (auto channel_id : range(ChannelId{num_channels})) + { + // Add nucleon-nucleon cross section parameters and data + ChannelXsData const& channel_data = this->get_channel_xs(channel_id); + CELER_ASSERT(channel_data.par.slope > 0); + xs_params.push_back(channel_data.par); + + GenericGridBuilder build_grid{&data.reals}; + make_builder(&data.nucleon_xs) + .push_back(build_grid(bins, make_span(channel_data.xs))); + } + CELER_ASSERT(data.nucleon_xs.size() == num_channels); + CELER_ASSERT(data.xs_params.size() == data.nucleon_xs.size()); + + // Move to mirrored data, copying to device + data_ = CollectionMirror{std::move(data)}; + CELER_ENSURE(this->data_); +} + +//---------------------------------------------------------------------------// +/*! + * Particle types and energy ranges that this model applies to. + */ +auto NeutronInelasticModel::applicability() const -> SetApplicability +{ + Applicability neutron_applic; + neutron_applic.particle = this->host_ref().scalars.neutron_id; + neutron_applic.lower = zero_quantity(); + neutron_applic.upper = this->host_ref().scalars.max_valid_energy(); + + return {neutron_applic}; +} + +//---------------------------------------------------------------------------// +/*! + * Get the microscopic cross sections for the given particle and material. + */ +auto NeutronInelasticModel::micro_xs(Applicability) const -> MicroXsBuilders +{ + // Cross sections are calculated on the fly + return {}; +} + +//---------------------------------------------------------------------------// +//!@{ +/*! + * Apply the interaction kernel. + */ +void NeutronInelasticModel::execute(CoreParams const&, CoreStateHost&) const +{ + CELER_NOT_IMPLEMENTED("Neutron inelastic interaction"); +} + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +void NeutronInelasticModel::execute(CoreParams const&, CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +/*! + * Get the acition ID for this model. + */ +ActionId NeutronInelasticModel::action_id() const +{ + return this->host_ref().scalars.action_id; +} + +//---------------------------------------------------------------------------// +/*! + * Get the particle-nucleon cross section (in barn) as a function of particle + * energy. Only neutron-neutron, neutron-proton and proton-proton channels are + * tabulated in [10, 320] (MeV) where pion production is not likely. The cross + * sections below 10 MeV will be calculated on the fly using the Stepanov's + * function. Tabulated data of cross sections and parameters at the low energy + * are from G4CascadePPChannel, G4CascadeNPChannel and G4CascadeNNChannel of + * the Geant4 11.2 release. Also note that the channel cross sections of + * nucleon-nucleon are same as their total cross sections in the energy range. + * + * H. W. Bertini, "Low-Energy Intranuclear Cascade Calculation", Phys. Rev. + * Vol. 131, page 1801 (1963). W. Hess, "Summary of High-Energy Nucleon- + * Nucleon Cross-Section Data", Rev. Mod. Phys. Vol. 30, page 368 (1958). + */ +auto NeutronInelasticModel::get_channel_xs(ChannelId ch_id) + -> ChannelXsData const& +{ + CELER_EXPECT(ch_id); + static ChannelXsData const channels[] + = {{{17.613, 4.00, {0.0069466, 9.0692, -5.0574}}, + {0.8633, + 0.6746, + 0.4952, + 0.3760, + 0.2854, + 0.2058, + 0.1357, + 0.0937, + 0.0691, + 0.0552, + 0.0445, + 0.0388, + 0.0351}}, + {{20.360, 1.92, {0.0053107, 3.0885, -1.1748}}, + {0.3024, + 0.2359, + 0.1733, + 0.1320, + 0.1007, + 0.0749, + 0.0519, + 0.0388, + 0.0316, + 0.0278, + 0.0252, + 0.0240, + 0.0233}}, + {{17.613, 4.00, {0.0069466, 9.0692, -5.0574}}, + {0.8633, + 0.6746, + 0.4952, + 0.3760, + 0.2854, + 0.2058, + 0.1357, + 0.0937, + 0.0691, + 0.0552, + 0.0445, + 0.0388, + 0.0351}}}; + return channels[ch_id.unchecked_get()]; +} + +//---------------------------------------------------------------------------// +/*! + * Get the energy bins (MeV) of nucleon-nucleon channel data in [10, 320] (MeV) + * from the G4PionNucSampler class. Note that the GeV unit is used in the + * Bertini cascade G4NucleiModel class. + */ +Span NeutronInelasticModel::get_channel_bins() const +{ + static ChannelArray const bins + = {10, 13, 18, 24, 32, 42, 56, 75, 100, 130, 180, 240, 320}; + + return make_span(bins); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/model/NeutronInelasticModel.cu b/src/celeritas/neutron/model/NeutronInelasticModel.cu new file mode 100644 index 0000000000..100167bf46 --- /dev/null +++ b/src/celeritas/neutron/model/NeutronInelasticModel.cu @@ -0,0 +1,26 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/model/NeutronInelasticModel.cu +//---------------------------------------------------------------------------// +#include "NeutronInelasticModel.hh" + +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Interact with device data. + */ +void NeutronInelasticModel::execute(CoreParams const& params, + CoreStateDevice& state) const +{ + CELER_NOT_IMPLEMENTED("Neutron inelastic interaction"); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/model/NeutronInelasticModel.hh b/src/celeritas/neutron/model/NeutronInelasticModel.hh new file mode 100644 index 0000000000..bfe247d4e5 --- /dev/null +++ b/src/celeritas/neutron/model/NeutronInelasticModel.hh @@ -0,0 +1,102 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/model/NeutronInelasticModel.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/data/CollectionMirror.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/mat/IsotopeView.hh" +#include "celeritas/mat/MaterialView.hh" +#include "celeritas/neutron/data/NeutronInelasticData.hh" +#include "celeritas/phys/AtomicNumber.hh" +#include "celeritas/phys/Model.hh" + +namespace celeritas +{ +struct ImportPhysicsVector; +class MaterialParams; +class ParticleParams; + +//---------------------------------------------------------------------------// +/*! + * Set up and launch the neutron inelastic model interaction. + */ +class NeutronInelasticModel final : public Model +{ + public: + //!@{ + using AtomicMassNumber = IsotopeView::AtomicMassNumber; + using MevEnergy = units::MevEnergy; + using ReadData = std::function; + using HostRef = NeutronInelasticHostRef; + using DeviceRef = NeutronInelasticDeviceRef; + //!@} + + public: + // Construct from model ID and other necessary data + NeutronInelasticModel(ActionId id, + ParticleParams const& particles, + MaterialParams const& materials, + ReadData load_data); + + // Particle types and energy ranges that this model applies to + SetApplicability applicability() const final; + + // Get the microscopic cross sections for the given particle and material + MicroXsBuilders micro_xs(Applicability) const final; + + //! Apply the interaction kernel to host data + void execute(CoreParams const&, CoreStateHost&) const final; + + // Apply the interaction kernel to device data + void execute(CoreParams const&, CoreStateDevice&) const final; + + // ID of the model + ActionId action_id() const final; + + //! Short name for the interaction kernel + std::string label() const final { return "neutron-inelastic-bertini"; } + + //! Short description of the post-step action + std::string description() const final + { + return "interact by neutron inelastic (Bertini)"; + } + + //!@{ + //! Access model data + HostRef const& host_ref() const { return data_.host_ref(); } + DeviceRef const& device_ref() const { return data_.device_ref(); } + //!@} + + private: + //// DATA //// + + // Host/device storage and reference + CollectionMirror data_; + + //// TYPES //// + + using HostXsData = HostVal; + using ChannelArray = Array; + + struct ChannelXsData + { + StepanovParameters par; + ChannelArray xs; + }; + + //// HELPER FUNCTIONS //// + + Span get_channel_bins() const; + static ChannelXsData const& get_channel_xs(ChannelId id); +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/process/NeutronInelasticProcess.cc b/src/celeritas/neutron/process/NeutronInelasticProcess.cc new file mode 100644 index 0000000000..5d5264af24 --- /dev/null +++ b/src/celeritas/neutron/process/NeutronInelasticProcess.cc @@ -0,0 +1,70 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/process/NeutronInelasticProcess.cc +//---------------------------------------------------------------------------// +#include "NeutronInelasticProcess.hh" + +#include "corecel/Assert.hh" +#include "celeritas/grid/ValueGridBuilder.hh" +#include "celeritas/neutron/model/NeutronInelasticModel.hh" +#include "celeritas/phys/PDGNumber.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct from host data. + */ +NeutronInelasticProcess::NeutronInelasticProcess(SPConstParticles particles, + SPConstMaterials materials, + ReadData load_data) + : particles_(std::move(particles)) + , materials_(std::move(materials)) + , load_data_(std::move(load_data)) + , neutron_id_(particles_->find(pdg::neutron())) +{ + CELER_EXPECT(particles_); + CELER_EXPECT(materials_); + CELER_EXPECT(load_data_); + CELER_ENSURE(neutron_id_); +} + +//---------------------------------------------------------------------------// +/*! + * Construct the models associated with this process. + */ +auto NeutronInelasticProcess::build_models(ActionIdIter id) const -> VecModel +{ + return {std::make_shared( + *id++, *particles_, *materials_, load_data_)}; +} + +//---------------------------------------------------------------------------// +/*! + * Get the inelastic interaction cross sections for the given energy range. + */ +auto NeutronInelasticProcess::step_limits(Applicability applic) const + -> StepLimitBuilders +{ + CELER_EXPECT(applic.particle == neutron_id_); + + // Cross sections are calculated on the fly + StepLimitBuilders builders; + builders[ValueGridType::macro_xs] = std::make_unique(); + return builders; +} + +//---------------------------------------------------------------------------// +/*! + * Name of the process. + */ +std::string NeutronInelasticProcess::label() const +{ + return "Neutron inelastic"; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/process/NeutronInelasticProcess.hh b/src/celeritas/neutron/process/NeutronInelasticProcess.hh new file mode 100644 index 0000000000..7f6d2f0b99 --- /dev/null +++ b/src/celeritas/neutron/process/NeutronInelasticProcess.hh @@ -0,0 +1,63 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/process/NeutronInelasticProcess.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "celeritas/mat/MaterialParams.hh" +#include "celeritas/phys/Applicability.hh" +#include "celeritas/phys/AtomicNumber.hh" +#include "celeritas/phys/ParticleParams.hh" +#include "celeritas/phys/Process.hh" + +namespace celeritas +{ +struct ImportPhysicsVector; + +//---------------------------------------------------------------------------// +/*! + * Inelastic interaction process for neutrons. + */ +class NeutronInelasticProcess : public Process +{ + public: + //!@{ + //! \name Type aliases + using SPConstParticles = std::shared_ptr; + using SPConstMaterials = std::shared_ptr; + using ReadData = std::function; + //!@} + + public: + // Construct from particle, material, and external cross section data + NeutronInelasticProcess(SPConstParticles particles, + SPConstMaterials materials, + ReadData load_data); + + // Construct the models associated with this process + VecModel build_models(ActionIdIter start_id) const final; + + // Get the interaction cross sections for the given energy range + StepLimitBuilders step_limits(Applicability range) const final; + + //! Whether to use the integral method to sample interaction length + bool use_integral_xs() const final { return false; } + + // Name of the process + std::string label() const final; + + private: + SPConstParticles particles_; + SPConstMaterials materials_; + ReadData load_data_; + ParticleId neutron_id_; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh b/src/celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh new file mode 100644 index 0000000000..992e0c75d4 --- /dev/null +++ b/src/celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh @@ -0,0 +1,84 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "corecel/math/Algorithms.hh" +#include "corecel/math/Quantity.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/Types.hh" +#include "celeritas/grid/GenericCalculator.hh" +#include "celeritas/neutron/data/NeutronInelasticData.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Calculate neutron inelastic cross sections from NeutronInelasticData + */ +class NeutronInelasticMicroXsCalculator +{ + public: + //!@{ + //! \name Type aliases + using ParamsRef = NeutronInelasticRef; + using Energy = units::MevEnergy; + using BarnXs = units::BarnXs; + //!@} + + public: + // Construct with shared and state data + inline CELER_FUNCTION + NeutronInelasticMicroXsCalculator(ParamsRef const& shared, Energy energy); + + // Compute cross section + inline CELER_FUNCTION BarnXs operator()(ElementId el_id) const; + + private: + // Shared constant physics properties + NeutronInelasticRef const& shared_; + // Incident neutron energy + real_type const inc_energy_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with shared and state data. + */ +CELER_FUNCTION +NeutronInelasticMicroXsCalculator::NeutronInelasticMicroXsCalculator( + ParamsRef const& shared, Energy energy) + : shared_(shared), inc_energy_(energy.value()) +{ +} + +//---------------------------------------------------------------------------// +/*! + * Compute microscopic (element) cross section + */ +CELER_FUNCTION +auto NeutronInelasticMicroXsCalculator::operator()(ElementId el_id) const + -> BarnXs +{ + CELER_EXPECT(el_id < shared_.micro_xs.size()); + + // Get element cross section data + GenericGridData grid = shared_.micro_xs[el_id]; + + // Calculate micro cross section at the given energy + GenericCalculator calc_xs(grid, shared_.reals); + real_type result = calc_xs(inc_energy_); + + return BarnXs{result}; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/neutron/xs/NucleonNucleonXsCalculator.hh b/src/celeritas/neutron/xs/NucleonNucleonXsCalculator.hh new file mode 100644 index 0000000000..32fc920bfa --- /dev/null +++ b/src/celeritas/neutron/xs/NucleonNucleonXsCalculator.hh @@ -0,0 +1,121 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/xs/NucleonNucleonXsCalculator.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "corecel/math/Quantity.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/Types.hh" +#include "celeritas/grid/GenericCalculator.hh" +#include "celeritas/grid/PolyEvaluator.hh" +#include "celeritas/neutron/data/NeutronInelasticData.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Calculate nucleon-nucleon (NN) cross sections from NeutronInelasticData + */ +class NucleonNucleonXsCalculator +{ + public: + //!@{ + //! \name Type aliases + using ParamsRef = NeutronInelasticRef; + using Energy = units::MevEnergy; + using BarnXs = units::BarnXs; + //!@} + + public: + // Construct with shared and state data + inline CELER_FUNCTION NucleonNucleonXsCalculator(ParamsRef const& shared); + + // Compute cross section + inline CELER_FUNCTION BarnXs operator()(ChannelId el_id, + Energy energy) const; + + static CELER_CONSTEXPR_FUNCTION Energy high_otf_energy() + { + return Energy{10}; + } + + static CELER_CONSTEXPR_FUNCTION Energy low_otf_energy() + { + return Energy{1}; + } + + private: + // Shared constant physics properties + NeutronInelasticRef const& shared_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with shared and state data. + */ +CELER_FUNCTION +NucleonNucleonXsCalculator::NucleonNucleonXsCalculator(ParamsRef const& shared) + : shared_(shared) +{ +} + +//---------------------------------------------------------------------------// +/*! + * Compute nucleon-nucleon (NN) cross section + * + * The parameterization of nucleon-nucleon cross sections below 10 MeV takes + * the following functional form, + * \f[ + * SF(E) = coeffs[0] + coeffs[1]/E + coeffs[2]/E^{2} + * \f] + * where the kinetic energy of the incident nucleon, \em E is in [1, 10] MeV. + * Below 1 MeV, \f$ SF(E) = slope/E \f$ down to \f$ E = slope/xs_zero \f$ while + * \f$ SF(E) = xs_zero \f$ if \em E is in [0, slope/xs_zero] MeV. + */ +CELER_FUNCTION +auto NucleonNucleonXsCalculator::operator()(ChannelId ch_id, + Energy energy) const -> BarnXs +{ + CELER_EXPECT(ch_id < shared_.nucleon_xs.size()); + real_type result; + + if (energy < this->high_otf_energy()) + { + // Calculate NN cross section according to the Stepanov's function + // for the incident nucleon kinetic energy below 10 MeV + StepanovParameters const& par = shared_.xs_params[ch_id]; + + if (energy <= this->low_otf_energy()) + { + result = celeritas::min(par.slope / energy.value(), par.xs_zero); + } + else + { + using StepanovFunction = PolyEvaluator; + result + = StepanovFunction(par.coeffs)(real_type{1} / energy.value()); + } + } + else + { + // Get tabulated NN cross section data for the given channel + GenericGridData grid = shared_.nucleon_xs[ch_id]; + + // Calculate NN cross section at the given energy + GenericCalculator calc_xs(grid, shared_.reals); + result = calc_xs(energy.value()); + } + + return BarnXs{result}; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/phys/ProcessBuilder.cc b/src/celeritas/phys/ProcessBuilder.cc index 8b9028b5ad..9beb3ee70d 100644 --- a/src/celeritas/phys/ProcessBuilder.cc +++ b/src/celeritas/phys/ProcessBuilder.cc @@ -186,7 +186,7 @@ auto ProcessBuilder::build_neutron_elastic() -> SPProcess { if (!read_neutron_elastic_) { - read_neutron_elastic_ = NeutronXsReader{}; + read_neutron_elastic_ = NeutronXsReader{NeutronXsType::el}; } return std::make_shared( diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index dfc6e51fbf..8d6e79e99a 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -294,6 +294,7 @@ celeritas_add_device_test(mat/Material) # Neutron set(CELERITASTEST_PREFIX celeritas/neutron) celeritas_add_test(neutron/NeutronElastic.test.cc ${_needs_double}) +celeritas_add_test(neutron/NeutronInelastic.test.cc ${_needs_double}) #-----------------------------------------------------------------------------# # Phys diff --git a/test/celeritas/data/inel2 b/test/celeritas/data/inel2 new file mode 100644 index 0000000000..5b53076684 --- /dev/null +++ b/test/celeritas/data/inel2 @@ -0,0 +1 @@ +9.64635696448e-07 20000 63 63 9.64635696448e-07 1.17880371137e-25 1.41589156877e-06 9.72048915471e-26 2.07824460767e-06 8.02173882156e-26 3.05044591308e-06 6.61531481989e-26 4.47744227714e-06 5.45824896871e-26 6.5719864952e-06 4.50369476421e-26 9.64635696448e-06 3.71407893581e-26 1.41589156877e-05 3.06281908529e-26 2.07824460767e-05 2.52232897257e-26 3.05044591308e-05 2.07971782031e-26 4.47744227714e-05 1.71285189505e-26 6.5719864952e-05 1.41138612063e-26 9.64635696448e-05 1.16344611295e-26 0.000141589156877 9.57013916217e-27 0.000207824460767 7.87467056962e-27 0.000305044591308 6.46708435307e-27 0.000447744227714 5.31115103968e-27 0.00065719864952 4.3573935224e-27 0.000964635696448 3.56739073802e-27 0.00141589156877 2.91788103858e-27 0.00207824460767 2.38174542826e-27 0.00305044591308 1.93857044637e-27 0.00447744227714 1.57391318819e-27 0.0065719864952 1.27387324998e-27 0.00964635696448 1.02703799147e-27 0.0141589156877 8.24211449901e-28 0.0207824460767 6.58010585037e-28 0.0305044591308 5.2276754645e-28 0.0447744227714 4.12523489279e-28 0.065719864952 3.24179614351e-28 0.0964635696448 2.54499742092e-28 0.141589156877 2.01341812626e-28 0.207824460767 1.64335722357e-28 0.305044591308 1.42551567516e-28 0.447744227714 1.32659927205e-28 0.65719864952 1.22086823946e-28 0.964635696448 1.14689171907e-28 1.41589156877 1.20739605185e-28 2.07824460767 1.1189485679e-28 3.05044591308 8.39582688757e-29 4.47744227714 5.78416535396e-29 6.5719864952 4.44559631216e-29 9.64635696448 3.48104952097e-29 14.1589156877 2.69043860876e-29 20.7824460767 2.24142651033e-24 30.5044591308 8.10089182616e-24 44.7744227714 1.14548845543e-23 65.719864952 9.4280135048e-24 96.4635696448 8.4e-24 141.589156877 8e-24 207.824460767 7.88435107847e-24 305.044591308 8.04035673046e-24 447.744227714 9.08646536628e-24 657.19864952 1.02859932476e-23 964.635696448 1.08e-23 1415.89156877 1.1132713255e-23 2078.24460767 1.14e-23 3050.44591308 1.13949554087e-23 4477.44227714 1.12522557723e-23 6571.9864952 1.10428013505e-23 9646.35696448 1.08235762024e-23 14158.9156877 1.07168216862e-23 20000 1.06e-23 diff --git a/test/celeritas/data/inel29 b/test/celeritas/data/inel29 new file mode 100644 index 0000000000..b7e3ca870a --- /dev/null +++ b/test/celeritas/data/inel29 @@ -0,0 +1 @@ +0.215955032466 20000 61 61 0.215955032466 2.49956000564e-28 0.261635494852 3.02945931644e-28 0.316978638492 1.17630913187e-27 0.384028387728 4.42813139534e-27 0.465261013431 8.36780400847e-27 0.563676586253 1.31408263452e-26 0.682909774767 9.8067228793e-25 0.827364080478 8.85905383354e-24 1.00237446725 2.18811450614e-23 1.21440439138 4.20398066486e-23 1.47128450892 6.26343145019e-23 1.78250187627 8.84627569445e-23 2.15955032466 1.05506633004e-22 2.61635494852 1.22146090483e-22 3.16978638492 1.36763027424e-22 3.84028387728 1.45143231872e-22 4.65261013431 1.49228632316e-22 5.63676586253 1.48910830442e-22 6.82909774767 1.45615577771e-22 8.27364080478 1.42987407953e-22 10.0237446725 1.36691295225e-22 12.1440439138 1.32329567798e-22 14.7128450892 1.40114140468e-22 17.8250187627 1.42307681642e-22 21.5955032466 1.40248232989e-22 26.1635494852 1.35091126287e-22 31.6978638492 1.29151068075e-22 38.4028387728 1.21661935379e-22 46.5261013431 1.06558237851e-22 56.3676586253 9.34529365499e-23 68.2909774767 8.7025413514e-23 82.7364080478 8.30895387928e-23 100.237446725 8.09881276637e-23 121.440439138 7.98559560862e-23 147.128450892 7.76435774554e-23 178.250187627 7.60874906187e-23 215.955032466 7.59361798701e-23 261.635494852 7.59628969279e-23 316.978638492 7.65e-23 384.028387728 7.68402838773e-23 465.261013431 7.86315253358e-23 563.676586253 8.04551487938e-23 682.909774767 8.22436466215e-23 827.364080478 8.32736408048e-23 1002.37446725 8.48104476559e-23 1214.40439138 8.57433793221e-23 1471.28450892 8.68736518392e-23 1782.50187627 8.7e-23 2159.55032466 8.69680899351e-23 2616.35494852 8.68767290103e-23 3169.78638492 8.65622990611e-23 3840.28387728 8.56236025718e-23 4652.61013431 8.4486345812e-23 5636.76586253 8.35224256031e-23 6829.09774767 8.26281766892e-23 8273.64080478 8.18631795976e-23 10023.7446725 8.09983378729e-23 12144.0439138 8.0849916926e-23 14712.8450892 8.06701008438e-23 17825.0187627 8.04522486866e-23 20000 8.03e-23 \ No newline at end of file diff --git a/test/celeritas/neutron/NeutronElastic.test.cc b/test/celeritas/neutron/NeutronElastic.test.cc index bba6036e0f..877039300a 100644 --- a/test/celeritas/neutron/NeutronElastic.test.cc +++ b/test/celeritas/neutron/NeutronElastic.test.cc @@ -40,7 +40,7 @@ class NeutronElasticTest : public NeutronTestBase // Load neutron elastic cross section data std::string data_path = this->test_data_path("celeritas", ""); - NeutronXsReader read_el_data(data_path.c_str()); + NeutronXsReader read_el_data(NeutronXsType::el, data_path.c_str()); // Set up the default particle: 100 MeV neutron along +z direction auto const& particles = *this->particle_params(); diff --git a/test/celeritas/neutron/NeutronInelastic.test.cc b/test/celeritas/neutron/NeutronInelastic.test.cc new file mode 100644 index 0000000000..8005ca7da9 --- /dev/null +++ b/test/celeritas/neutron/NeutronInelastic.test.cc @@ -0,0 +1,179 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/neutron/NeutronInelastic.test.cc +//---------------------------------------------------------------------------// +#include + +#include "corecel/cont/Range.hh" +#include "corecel/data/Ref.hh" +#include "corecel/math/ArrayUtils.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/grid/GenericGridData.hh" +#include "celeritas/io/NeutronXsReader.hh" +#include "celeritas/mat/MaterialTrackView.hh" +#include "celeritas/neutron/NeutronTestBase.hh" +#include "celeritas/neutron/interactor/NeutronInelasticInteractor.hh" +#include "celeritas/neutron/model/NeutronInelasticModel.hh" +#include "celeritas/neutron/xs/NeutronInelasticMicroXsCalculator.hh" +#include "celeritas/neutron/xs/NucleonNucleonXsCalculator.hh" +#include "celeritas/phys/MacroXsCalculator.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +class NeutronInelasticTest : public NeutronTestBase +{ + protected: + using MevEnergy = units::MevEnergy; + using SPConstNInelasticModel = std::shared_ptr; + + void SetUp() override + { + using namespace units; + + // Load neutron elastic cross section data + std::string data_path = this->test_data_path("celeritas", ""); + NeutronXsReader read_el_data(NeutronXsType::inel, data_path.c_str()); + + // Set up the default particle: 100 MeV neutron along +z direction + auto const& particles = *this->particle_params(); + this->set_inc_particle(pdg::neutron(), MevEnergy{100}); + this->set_inc_direction({0, 0, 1}); + + // Set up the default material + this->set_material("HeCu"); + model_ = std::make_shared( + ActionId{0}, particles, *this->material_params(), read_el_data); + } + + protected: + SPConstNInelasticModel model_; +}; + +//---------------------------------------------------------------------------// +// TESTS +//---------------------------------------------------------------------------// +TEST_F(NeutronInelasticTest, micro_xs) +{ + // Calculate the elastic neutron-nucleus microscopic cross section + using XsCalculator = NeutronInelasticMicroXsCalculator; + + // Set the target element: Cu + ElementId el_id{1}; + + // Check the size of the element cross section data (G4PARTICLEXS4.0) + // The neutron/inelZ data are pruned by a factor of 5 for this test + NeutronInelasticRef shared = model_->host_ref(); + GenericGridData grid = shared.micro_xs[el_id]; + EXPECT_EQ(grid.grid.size(), 61); + + // Microscopic cross section (units::BarnXs) in [1e-04:1e+4] (MeV) + std::vector const expected_micro_xs = {2.499560005640001e-06, + 2.499560005640001e-06, + 2.499560005640001e-06, + 2.499560005640001e-06, + 0.2170446680979802, + 1.3677671823188946, + 0.81016638725225387, + 0.84789596907525477}; + + real_type energy = 1e-4; + real_type const factor = 1e+1; + for (auto i : range(expected_micro_xs.size())) + { + XsCalculator calc_micro_xs(shared, MevEnergy{energy}); + EXPECT_SOFT_EQ(calc_micro_xs(el_id).value(), expected_micro_xs[i]); + energy *= factor; + } + + // Check the elastic cross section at the upper bound (20 GeV) + XsCalculator calc_upper_xs(shared, MevEnergy{2e+4}); + EXPECT_SOFT_EQ(calc_upper_xs(el_id).value(), 0.80300000000000027); +} + +TEST_F(NeutronInelasticTest, macro_xs) +{ + // Calculate the inelastic neutron-nucleus macroscopic cross section + auto material = this->material_track().make_material_view(); + auto calc_xs = MacroXsCalculator( + model_->host_ref(), material); + + // Macroscopic cross section (\f$ cm^{-1} \f$) in [1e-04:1e+4] (MeV) + std::vector const expected_macro_xs = {1.0577605656430734e-06, + 4.4447010621996484e-07, + 2.5134945234021254e-07, + 1.9270371228950039e-07, + 0.015057496086707027, + 0.094888935102106969, + 0.056850427191922973, + 0.059657345679963072}; + + real_type energy = 1e-4; + real_type const factor = 1e+1; + for (auto i : range(expected_macro_xs.size())) + { + EXPECT_SOFT_EQ( + native_value_to(calc_xs(MevEnergy{energy})).value(), + expected_macro_xs[i]); + energy *= factor; + } + + // Check the neutron inelastic interaction cross section at the upper bound + // (20 GeV) + EXPECT_SOFT_EQ( + native_value_to(calc_xs(MevEnergy{2000})).value(), + 0.061219850473480573); +} + +TEST_F(NeutronInelasticTest, nucleon_xs) +{ + // Calculate nucleon-nucleon cross sections (units::BarnXs) + NeutronInelasticRef shared = model_->host_ref(); + + NucleonNucleonXsCalculator calc_xs(shared); + size_type num_channels = shared.scalars.num_channels(); + EXPECT_EQ(num_channels, 3); + + std::vector xs_zero; + std::vector xs; + for (auto ch_id : range(ChannelId{num_channels})) + { + xs_zero.push_back(shared.xs_params[ch_id].xs_zero); + for (real_type inc_e : {0.01, 0.1, 1., 10., 100., 320.}) + { + xs.push_back(calc_xs(ch_id, MevEnergy{inc_e}).value()); + } + } + real_type const expected_xs_zero[] = {17.613, 20.36, 17.613}; + real_type const expected_xs[] = {17.613, + 17.613, + 4.0, + 0.8633, + 0.0691, + 0.0351, + 20.36, + 19.2, + 1.92, + 0.3024, + 0.0316, + 0.0233, + 17.613, + 17.613, + 4.0, + 0.8633, + 0.0691, + 0.0351}; + EXPECT_VEC_SOFT_EQ(expected_xs_zero, xs_zero); + EXPECT_VEC_SOFT_EQ(expected_xs, xs); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas From 225cba42faed9e5fb2d688ed40b3b6b437d70e35 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 23 Apr 2024 19:40:33 -0400 Subject: [PATCH 14/59] Move unit enumeration to corecel and improve assertion flexibility (#1197) * Move pinned allocator to subdirectory * Make MemSpace compatible with enum string mapper * Move unit system enums to corecel * Reformat assert messages * Replace runtime error enumeration with a string * Change debug error to runtime error --- app/celer-g4/ExceptionHandler.cc | 11 +- src/celeritas/Types.cc | 29 --- src/celeritas/Types.hh | 18 -- src/celeritas/ext/ScopedRootErrorHandler.cc | 8 +- src/celeritas/field/RZMapFieldInputIO.json.cc | 1 + src/corecel/Assert.cc | 173 +++++------------- src/corecel/Assert.hh | 162 +++++++--------- src/corecel/AssertIO.json.cc | 2 +- src/corecel/CMakeLists.txt | 3 +- src/corecel/Types.cc | 55 ++++++ src/corecel/Types.hh | 41 ++++- src/corecel/{ => data}/PinnedAllocator.cc | 8 +- src/corecel/io/EnumStringMapper.hh | 2 + src/geocel/ScopedGeantExceptionHandler.cc | 10 +- .../corecel/sys/MultiExceptionHandler.test.cc | 19 +- test/orange/g4org/SolidConverter.test.cc | 6 +- test/orange/orangeinp/ConvexRegion.test.cc | 7 +- 17 files changed, 246 insertions(+), 309 deletions(-) create mode 100644 src/corecel/Types.cc rename src/corecel/{ => data}/PinnedAllocator.cc (84%) diff --git a/app/celer-g4/ExceptionHandler.cc b/app/celer-g4/ExceptionHandler.cc index 840e4f3285..807f83a8a3 100644 --- a/app/celer-g4/ExceptionHandler.cc +++ b/app/celer-g4/ExceptionHandler.cc @@ -44,8 +44,15 @@ G4bool ExceptionHandler::Notify(char const* origin_of_exception, CELER_EXPECT(exception_code); // Construct message - auto err = RuntimeError::from_geant_exception( - origin_of_exception, exception_code, description); + auto err = RuntimeError{[&] { + RuntimeErrorDetails details; + details.which = "Geant4"; + details.what = description; + details.condition = exception_code; + details.file = origin_of_exception; + return details; + }()}; + bool must_abort{false}; switch (severity) diff --git a/src/celeritas/Types.cc b/src/celeritas/Types.cc index d3e318fef0..a1616dcb39 100644 --- a/src/celeritas/Types.cc +++ b/src/celeritas/Types.cc @@ -8,40 +8,11 @@ #include "Types.hh" #include "corecel/io/EnumStringMapper.hh" -#include "corecel/io/StringEnumMapper.hh" #include "UnitTypes.hh" namespace celeritas { -//---------------------------------------------------------------------------// -/*! - * Get a string corresponding to a unit system. - */ -char const* to_cstring(UnitSystem value) -{ - static_assert(static_cast(UnitSystem::cgs) == CELERITAS_UNITS_CGS); - static_assert(static_cast(UnitSystem::si) == CELERITAS_UNITS_SI); - static_assert(static_cast(UnitSystem::clhep) == CELERITAS_UNITS_CLHEP); - static_assert(static_cast(UnitSystem::native) == CELERITAS_UNITS); - - static EnumStringMapper const to_cstring_impl{ - "none", "cgs", "si", "clhep"}; - return to_cstring_impl(value); -} - -//---------------------------------------------------------------------------// -/*! - * Get a unit system corresponding to a string value. - */ -UnitSystem to_unit_system(std::string const& s) -{ - static auto const from_string - = StringEnumMapper::from_cstring_func(to_cstring, - "unit system"); - return from_string(s); -} - //---------------------------------------------------------------------------// /*! * Get a string corresponding to an interpolation. diff --git a/src/celeritas/Types.hh b/src/celeritas/Types.hh index 4b8015232d..9b154de233 100644 --- a/src/celeritas/Types.hh +++ b/src/celeritas/Types.hh @@ -77,18 +77,6 @@ using ChannelId = OpaqueId; //---------------------------------------------------------------------------// // ENUMERATIONS -//---------------------------------------------------------------------------// -//! Unit system used by Celeritas -enum class UnitSystem -{ - none, //!< Invalid unit system - cgs, //!< Gaussian CGS - si, //!< International System - clhep, //!< Geant4 native - size_, - native = CELERITAS_UNITS, //!< Compile time selected system -}; - //---------------------------------------------------------------------------// //! Interpolation type enum class Interp @@ -193,15 +181,9 @@ struct StepLimit // HELPER FUNCTIONS (HOST) //---------------------------------------------------------------------------// -// Get a string corresponding to a unit system -char const* to_cstring(UnitSystem); - // Get a string corresponding to an interpolation char const* to_cstring(Interp); -// Get a unit system corresponding to a string -UnitSystem to_unit_system(std::string const& s); - // Get a string corresponding to a material state char const* to_cstring(MatterState); diff --git a/src/celeritas/ext/ScopedRootErrorHandler.cc b/src/celeritas/ext/ScopedRootErrorHandler.cc index 1d0e1b3130..dd4d736e03 100644 --- a/src/celeritas/ext/ScopedRootErrorHandler.cc +++ b/src/celeritas/ext/ScopedRootErrorHandler.cc @@ -58,7 +58,13 @@ void RootErrorHandler(Int_t rootlevel, if (must_abort) { - throw RuntimeError::from_root_error(location, msg); + throw RuntimeError{[&] { + RuntimeErrorDetails details; + details.which = "ROOT"; + details.what = msg; + details.file = location; + return details; + }()}; } else { diff --git a/src/celeritas/field/RZMapFieldInputIO.json.cc b/src/celeritas/field/RZMapFieldInputIO.json.cc index 6ecc54bdfd..cd1f8acd9e 100644 --- a/src/celeritas/field/RZMapFieldInputIO.json.cc +++ b/src/celeritas/field/RZMapFieldInputIO.json.cc @@ -12,6 +12,7 @@ #include #include +#include "corecel/Types.hh" #include "corecel/cont/Range.hh" #include "corecel/io/JsonUtils.json.hh" #include "corecel/io/Logger.hh" diff --git a/src/corecel/Assert.cc b/src/corecel/Assert.cc index 5e550ce069..92651c3cb7 100644 --- a/src/corecel/Assert.cc +++ b/src/corecel/Assert.cc @@ -23,21 +23,6 @@ namespace celeritas { namespace { -//---------------------------------------------------------------------------// -/*! - * Return whether to give an extra verbose message. - */ -bool determine_verbose_message() -{ -#if CELERITAS_DEBUG - // Always verbose if debug flags are enabled - return true; -#else - // Verbose if the CELER_LOG environment variable is defined - return !celeritas::getenv("CELER_LOG").empty(); -#endif -} - //---------------------------------------------------------------------------// /*! * Construct a debug assertion message for printing. @@ -64,33 +49,44 @@ std::string build_debug_error_msg(DebugErrorDetails const& d) */ std::string build_runtime_error_msg(RuntimeErrorDetails const& d) { - static bool const verbose_message = determine_verbose_message(); + static bool const verbose_message = [] { +#if CELERITAS_DEBUG + // Always verbose if debug flags are enabled + return true; +#else + // Verbose if the CELER_LOG environment variable is defined + return !celeritas::getenv("CELER_LOG").empty(); +#endif + }(); std::ostringstream msg; - if (d.which != RuntimeErrorType::validate || verbose_message) + msg << "celeritas: " << color_code('R') + << (d.which.empty() ? "unknown" : d.which) + << " error: " << color_code(' '); + if (d.which == "configuration") { - msg << color_code('W') << d.file; - if (d.line) - { - msg << ':' << d.line; - } - msg << ':' << color_code(' ') << '\n'; + msg << "required dependency is disabled in this build: "; } - - msg << "celeritas: " << color_code('R') << to_cstring(d.which) - << " error: "; - if (verbose_message || d.what.empty()) + else if (d.which == "not implemented") { - msg << color_code('x') << d.condition << color_code(' ') << " failed"; - if (!d.what.empty()) - msg << ":\n "; + msg << "feature is not yet implemented: "; } - else + msg << d.what; + + if (verbose_message || d.what.empty() || d.which != "runtime") { - msg << color_code(' '); + msg << '\n' + << color_code('W') << (d.file.empty() ? "unknown source" : d.file); + if (d.line) + { + msg << ':' << d.line; + } + if (!d.condition.empty()) + { + msg << ':' << color_code(' ') << " '" << d.condition << "' failed"; + } } - msg << d.what; return msg.str(); } @@ -111,10 +107,6 @@ char const* to_cstring(DebugErrorType which) return "internal assertion failed"; case DebugErrorType::unreachable: return "unreachable code point"; - case DebugErrorType::unconfigured: - return "required dependency is disabled in this build"; - case DebugErrorType::unimplemented: - return "feature is not yet implemented"; case DebugErrorType::postcondition: return "postcondition failed"; case DebugErrorType::assumption: @@ -125,118 +117,37 @@ char const* to_cstring(DebugErrorType which) //---------------------------------------------------------------------------// /*! - * Get a human-readable string describing a runtime error. + * Get an MPI error string. */ -char const* to_cstring(RuntimeErrorType which) +std::string mpi_error_to_string(int errorcode) { - switch (which) - { - case RuntimeErrorType::validate: - return "runtime"; - case RuntimeErrorType::device: -#if CELERITAS_USE_CUDA - return "CUDA"; +#if CELERITAS_USE_MPI + std::string error_string; + error_string.resize(MPI_MAX_ERROR_STRING); + int length = 0; + MPI_Error_string(errorcode, &error_string.front(), &length); + error_string.resize(length); + return error_string; #else - return "device"; + CELER_DISCARD(errorcode); + CELER_NOT_CONFIGURED("MPI"); #endif - case RuntimeErrorType::mpi: - return "MPI"; - case RuntimeErrorType::geant: - return "Geant4"; - case RuntimeErrorType::root: - return "ROOT"; - } - return ""; } //---------------------------------------------------------------------------// /*! * Construct a debug exception from detailed attributes. */ -DebugError::DebugError(DebugErrorDetails d) +DebugError::DebugError(DebugErrorDetails&& d) : std::logic_error(build_debug_error_msg(d)), details_(std::move(d)) { } -//---------------------------------------------------------------------------// -/*! - * Construct a runtime exception from a validation failure. - */ -RuntimeError RuntimeError::from_validate(std::string what, - char const* code, - char const* file, - int line) -{ - return RuntimeError{{RuntimeErrorType::validate, what, code, file, line}}; -} - -//---------------------------------------------------------------------------// -/*! - * Construct a runtime exception from a CUDA/HIP runtime failure. - */ -RuntimeError RuntimeError::from_device_call(char const* error_string, - char const* code, - char const* file, - int line) -{ - return RuntimeError{ - {RuntimeErrorType::device, error_string, code, file, line}}; -} - -//---------------------------------------------------------------------------// -/*! - * Construct a message and throw an error from a runtime MPI failure. - */ -RuntimeError RuntimeError::from_mpi_call([[maybe_unused]] int errorcode, - char const* code, - char const* file, - int line) -{ - std::string error_string; -#if CELERITAS_USE_MPI - { - error_string.resize(MPI_MAX_ERROR_STRING); - int length = 0; - MPI_Error_string(errorcode, &error_string.front(), &length); - error_string.resize(length); - } -#endif - return RuntimeError{ - {RuntimeErrorType::mpi, error_string, code, file, line}}; -} - -//---------------------------------------------------------------------------// -/*! - * Construct an error message from a Geant4 exception. - * - * \param origin Usually the function that throws - * \param code A computery error code - * \param desc Description of the failure - */ -RuntimeError RuntimeError::from_geant_exception(char const* origin, - char const* code, - char const* desc) -{ - return RuntimeError{{RuntimeErrorType::geant, desc, code, origin, 0}}; -} - -//---------------------------------------------------------------------------// -/*! - * Construct an error message from a Geant4 exception. - * - * \param origin Usually the function that throws - * \param msg Description of the failure - */ -RuntimeError RuntimeError::from_root_error(char const* origin, char const* msg) -{ - return RuntimeError{{RuntimeErrorType::root, msg, nullptr, origin, 0}}; -} - //---------------------------------------------------------------------------// /*! * Construct a runtime error from detailed descriptions. */ -RuntimeError::RuntimeError(RuntimeErrorDetails d) +RuntimeError::RuntimeError(RuntimeErrorDetails&& d) : std::runtime_error(build_runtime_error_msg(d)), details_(std::move(d)) { } diff --git a/src/corecel/Assert.hh b/src/corecel/Assert.hh index 98ebb7f804..5deed98dfd 100644 --- a/src/corecel/Assert.hh +++ b/src/corecel/Assert.hh @@ -189,6 +189,21 @@ ::celeritas::unreachable(); \ } while (0) +#if !CELER_DEVICE_COMPILE || defined(__HIP__) +# define CELER_RUNTIME_THROW(WHICH, WHAT, COND) \ + throw ::celeritas::RuntimeError({ \ + WHICH, \ + WHAT, \ + COND, \ + __FILE__, \ + __LINE__, \ + }) +#else +# define CELER_RUNTIME_THROW(WHICH, WHAT, COND) \ + CELER_DEBUG_FAIL("Runtime errors cannot be thrown from device code", \ + unreachable); +#endif + #if CELERITAS_DEBUG # define CELER_EXPECT(COND) CELER_DEBUG_ASSERT_(COND, precondition) # define CELER_ASSERT(COND) CELER_DEBUG_ASSERT_(COND, internal) @@ -205,25 +220,26 @@ #endif #if !CELER_DEVICE_COMPILE || defined(__HIP__) -# define CELER_VALIDATE(COND, MSG) \ - do \ - { \ - if (CELER_UNLIKELY(!(COND))) \ - { \ - std::ostringstream celer_runtime_msg_; \ - celer_runtime_msg_ MSG; \ - throw ::celeritas::RuntimeError::from_validate( \ - celer_runtime_msg_.str(), #COND, __FILE__, __LINE__); \ - } \ +# define CELER_VALIDATE(COND, MSG) \ + do \ + { \ + if (CELER_UNLIKELY(!(COND))) \ + { \ + std::ostringstream celer_runtime_msg_; \ + celer_runtime_msg_ MSG; \ + CELER_RUNTIME_THROW( \ + "runtime", celer_runtime_msg_.str(), #COND); \ + } \ } while (0) #else -# define CELER_VALIDATE(COND, MSG) \ - CELER_DEBUG_FAIL("CELER_VALIDATE cannot be called from device code", \ - unreachable); +# define CELER_VALIDATE(COND, MSG) \ + CELER_RUNTIME_THROW("unreachable", "", #COND) #endif -#define CELER_NOT_CONFIGURED(WHAT) CELER_DEBUG_FAIL(WHAT, unconfigured) -#define CELER_NOT_IMPLEMENTED(WHAT) CELER_DEBUG_FAIL(WHAT, unimplemented) +#define CELER_NOT_CONFIGURED(WHAT) \ + CELER_RUNTIME_THROW("configuration", WHAT, {}) +#define CELER_NOT_IMPLEMENTED(WHAT) \ + CELER_RUNTIME_THROW("not implemented", WHAT, {}) /*! * \def CELER_CUDA_CALL @@ -246,19 +262,15 @@ * corecel/device_runtime_api.h . */ #if CELERITAS_USE_CUDA -# define CELER_CUDA_CALL(STATEMENT) \ - do \ - { \ - cudaError_t cuda_result_ = (STATEMENT); \ - if (CELER_UNLIKELY(cuda_result_ != cudaSuccess)) \ - { \ - cuda_result_ = cudaGetLastError(); \ - throw ::celeritas::RuntimeError::from_device_call( \ - cudaGetErrorString(cuda_result_), \ - #STATEMENT, \ - __FILE__, \ - __LINE__); \ - } \ +# define CELER_CUDA_CALL(STATEMENT) \ + do \ + { \ + cudaError_t cuda_result_ = (STATEMENT); \ + if (CELER_UNLIKELY(cuda_result_ != cudaSuccess)) \ + { \ + CELER_RUNTIME_THROW( \ + "CUDA", cudaGetErrorString(cuda_result_), #STATEMENT); \ + } \ } while (0) #else # define CELER_CUDA_CALL(STATEMENT) \ @@ -288,19 +300,16 @@ * declaration enforces this when HIP is disabled. */ #if CELERITAS_USE_HIP -# define CELER_HIP_CALL(STATEMENT) \ - do \ - { \ - hipError_t hip_result_ = (STATEMENT); \ - if (CELER_UNLIKELY(hip_result_ != hipSuccess)) \ - { \ - hip_result_ = hipGetLastError(); \ - throw ::celeritas::RuntimeError::from_device_call( \ - hipGetErrorString(hip_result_), \ - #STATEMENT, \ - __FILE__, \ - __LINE__); \ - } \ +# define CELER_HIP_CALL(STATEMENT) \ + do \ + { \ + hipError_t hip_result_ = (STATEMENT); \ + if (CELER_UNLIKELY(hip_result_ != hipSuccess)) \ + { \ + hip_result_ = hipGetLastError(); \ + CELER_RUNTIME_THROW( \ + "HIP", hipGetErrorString(hip_result_), #STATEMENT); \ + } \ } while (0) #else # define CELER_HIP_CALL(STATEMENT) \ @@ -360,15 +369,15 @@ * \note A file that uses this macro must include \c mpi.h. */ #if CELERITAS_USE_MPI -# define CELER_MPI_CALL(STATEMENT) \ - do \ - { \ - int mpi_result_ = (STATEMENT); \ - if (CELER_UNLIKELY(mpi_result_ != MPI_SUCCESS)) \ - { \ - throw ::celeritas::RuntimeError::from_mpi_call( \ - mpi_result_, #STATEMENT, __FILE__, __LINE__); \ - } \ +# define CELER_MPI_CALL(STATEMENT) \ + do \ + { \ + int mpi_result_ = (STATEMENT); \ + if (CELER_UNLIKELY(mpi_result_ != MPI_SUCCESS)) \ + { \ + CELER_RUNTIME_THROW( \ + "MPI", mpi_error_to_string(mpi_result_), #STATEMENT); \ + } \ } while (0) #else # define CELER_MPI_CALL(STATEMENT) \ @@ -392,21 +401,10 @@ enum class DebugErrorType precondition, //!< Precondition contract violation internal, //!< Internal assertion check failure unreachable, //!< Internal assertion: unreachable code path - unconfigured, //!< Internal assertion: required feature not enabled - unimplemented, //!< Internal assertion: not yet implemented postcondition, //!< Postcondition contract violation assumption, //!< "Assume" violation }; -enum class RuntimeErrorType -{ - validate, //!< Celeritas runtime error - device, //!< CUDA or HIP - mpi, //!< Coarse-grain parallelism - geant, //!< Error from Geant4 - root //!< Error from ROOT -}; - //! Detailed properties of a debug assertion failure struct DebugErrorDetails { @@ -419,11 +417,11 @@ struct DebugErrorDetails //! Detailed properties of a runtime error struct RuntimeErrorDetails { - RuntimeErrorType which{RuntimeErrorType::validate}; - std::string what{}; - std::string condition{}; - std::string file{}; - int line{0}; + std::string which{}; //!< Type of error (runtime, Geant4, MPI) + std::string what{}; //!< Descriptive message + std::string condition{}; //!< Code/test that failed + std::string file{}; //!< Source file + int line{0}; //!< Source line }; //---------------------------------------------------------------------------// @@ -436,11 +434,11 @@ struct RuntimeErrorDetails CELER_UNREACHABLE; } -//! Get a pretty string version of a debug error +// Get a pretty string version of a debug error char const* to_cstring(DebugErrorType which); -//! Get a pretty string version of a runtime error -char const* to_cstring(RuntimeErrorType which); +// Get an MPI error string +std::string mpi_error_to_string(int); //---------------------------------------------------------------------------// // TYPES @@ -456,7 +454,7 @@ class DebugError : public std::logic_error { public: // Construct from debug attributes - explicit DebugError(DebugErrorDetails); + explicit DebugError(DebugErrorDetails&&); //! Access the debug data DebugErrorDetails const& details() const { return details_; } @@ -472,30 +470,8 @@ class DebugError : public std::logic_error class RuntimeError : public std::runtime_error { public: - // Construct from validation failure - static RuntimeError - from_validate(std::string msg, char const* code, char const* file, int line); - - // Construct from device call - static RuntimeError from_device_call(char const* error_string, - char const* code, - char const* file, - int line); - - // Construct from MPI call - static RuntimeError - from_mpi_call(int errorcode, char const* code, char const* file, int line); - - // Construct from call to Geant4 - static RuntimeError from_geant_exception(char const* origin, - char const* code, - char const* desc); - - // Construct from call to ROOT - static RuntimeError from_root_error(char const* origin, char const* msg); - // Construct from details - explicit RuntimeError(RuntimeErrorDetails); + explicit RuntimeError(RuntimeErrorDetails&&); //! Access detailed information RuntimeErrorDetails const& details() const { return details_; } diff --git a/src/corecel/AssertIO.json.cc b/src/corecel/AssertIO.json.cc index 2d8f31444f..8f110ecd7f 100644 --- a/src/corecel/AssertIO.json.cc +++ b/src/corecel/AssertIO.json.cc @@ -39,7 +39,7 @@ void to_json(nlohmann::json& j, DebugErrorDetails const& d) void to_json(nlohmann::json& j, RuntimeErrorDetails const& d) { j["what"] = d.what; - j["which"] = to_cstring(d.which); + j["which"] = d.which; if (!d.condition.empty()) { j["condition"] = d.condition; diff --git a/src/corecel/CMakeLists.txt b/src/corecel/CMakeLists.txt index 0f281adce8..3cfe73aaf4 100644 --- a/src/corecel/CMakeLists.txt +++ b/src/corecel/CMakeLists.txt @@ -14,9 +14,10 @@ set(PUBLIC_DEPS) list(APPEND SOURCES Assert.cc - PinnedAllocator.cc + Types.cc data/Copier.cc data/DeviceAllocation.cc + data/PinnedAllocator.cc io/BuildOutput.cc io/ColorUtils.cc io/ExceptionOutput.cc diff --git a/src/corecel/Types.cc b/src/corecel/Types.cc new file mode 100644 index 0000000000..6a6250affc --- /dev/null +++ b/src/corecel/Types.cc @@ -0,0 +1,55 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file corecel/Types.cc +//---------------------------------------------------------------------------// +#include "Types.hh" + +#include "corecel/io/EnumStringMapper.hh" +#include "corecel/io/StringEnumMapper.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Get a string corresponding to a memory space. + */ +char const* to_cstring(MemSpace value) +{ + static EnumStringMapper const to_cstring_impl{ + "host", "device", "mapped"}; + return to_cstring_impl(value); +} + +//---------------------------------------------------------------------------// +/*! + * Get a string corresponding to a unit system. + */ +char const* to_cstring(UnitSystem value) +{ + static_assert(static_cast(UnitSystem::cgs) == CELERITAS_UNITS_CGS); + static_assert(static_cast(UnitSystem::si) == CELERITAS_UNITS_SI); + static_assert(static_cast(UnitSystem::clhep) == CELERITAS_UNITS_CLHEP); + static_assert(static_cast(UnitSystem::native) == CELERITAS_UNITS); + + static EnumStringMapper const to_cstring_impl{ + "none", "cgs", "si", "clhep"}; + return to_cstring_impl(value); +} + +//---------------------------------------------------------------------------// +/*! + * Get a unit system corresponding to a string value. + */ +UnitSystem to_unit_system(std::string const& s) +{ + static auto const from_string + = StringEnumMapper::from_cstring_func(to_cstring, + "unit system"); + return from_string(s); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/corecel/Types.hh b/src/corecel/Types.hh index 13952644b6..cd71110420 100644 --- a/src/corecel/Types.hh +++ b/src/corecel/Types.hh @@ -3,12 +3,20 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file corecel/Types.hh -//! \brief Type definitions for common Celeritas functionality +/*! + * \file corecel/Types.hh + * \brief Type definitions for common Celeritas functionality. + * + * This file includes types and properties particular to the build + * configuration. + */ //---------------------------------------------------------------------------// #pragma once #include +#include + +#include "celeritas_config.h" #include "Macros.hh" @@ -43,6 +51,7 @@ enum class MemSpace host, //!< CPU memory device, //!< GPU memory mapped, //!< Unified virtual address space (both host and device) + size_, #ifdef CELER_DEVICE_SOURCE native = device, //!< When included by a CUDA/HIP file; else 'host' #else @@ -58,6 +67,18 @@ enum class Ownership const_reference, //!< Immutable reference to the data }; +//---------------------------------------------------------------------------// +//! Unit system used by Celeritas +enum class UnitSystem +{ + none, //!< Invalid unit system + cgs, //!< Gaussian CGS + si, //!< International System + clhep, //!< Geant4 native + size_, + native = CELERITAS_UNITS, //!< Compile time selected system +}; + #if !defined(SWIG) || SWIG_VERSION > 0x050000 //---------------------------------------------------------------------------// //!@{ @@ -104,14 +125,14 @@ using RefPtr = ObserverPtr, M>; // HELPER FUNCTIONS (HOST) //---------------------------------------------------------------------------// -//! Get a string corresponding to a memory space -inline constexpr char const* to_cstring(MemSpace m) -{ - return m == MemSpace::host ? "host" - : m == MemSpace::device ? "device" - : m == MemSpace::mapped ? "mapped" - : nullptr; -} +// Get a string corresponding to a memory space +char const* to_cstring(MemSpace m); + +// Get a string corresponding to a unit system +char const* to_cstring(UnitSystem); + +// Get a unit system corresponding to a string +UnitSystem to_unit_system(std::string const& s); //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/corecel/PinnedAllocator.cc b/src/corecel/data/PinnedAllocator.cc similarity index 84% rename from src/corecel/PinnedAllocator.cc rename to src/corecel/data/PinnedAllocator.cc index 1e1be482c5..f13de5b858 100644 --- a/src/corecel/PinnedAllocator.cc +++ b/src/corecel/data/PinnedAllocator.cc @@ -3,11 +3,11 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file corecel/PinnedAllocator.cc +//! \file corecel/data/PinnedAllocator.cc //---------------------------------------------------------------------------// +#include "corecel/Types.hh" -#include "Types.hh" -#include "data/PinnedAllocator.t.hh" +#include "PinnedAllocator.t.hh" namespace celeritas { @@ -16,4 +16,4 @@ namespace celeritas template struct PinnedAllocator; template struct PinnedAllocator; //---------------------------------------------------------------------------// -} // namespace celeritas \ No newline at end of file +} // namespace celeritas diff --git a/src/corecel/io/EnumStringMapper.hh b/src/corecel/io/EnumStringMapper.hh index 9a1ccb800d..ec15506323 100644 --- a/src/corecel/io/EnumStringMapper.hh +++ b/src/corecel/io/EnumStringMapper.hh @@ -7,6 +7,8 @@ //---------------------------------------------------------------------------// #pragma once +#include + #include "corecel/Assert.hh" #include "corecel/cont/Array.hh" diff --git a/src/geocel/ScopedGeantExceptionHandler.cc b/src/geocel/ScopedGeantExceptionHandler.cc index bb5d3b80ee..076a96bf25 100644 --- a/src/geocel/ScopedGeantExceptionHandler.cc +++ b/src/geocel/ScopedGeantExceptionHandler.cc @@ -53,8 +53,14 @@ G4bool GeantExceptionHandler::Notify(char const* origin_of_exception, CELER_EXPECT(exception_code); // Construct message - auto err = RuntimeError::from_geant_exception( - origin_of_exception, exception_code, description); + auto err = RuntimeError{[&] { + RuntimeErrorDetails details; + details.which = "Geant4"; + details.what = description; + details.condition = exception_code; + details.file = origin_of_exception; + return details; + }()}; switch (severity) { diff --git a/test/corecel/sys/MultiExceptionHandler.test.cc b/test/corecel/sys/MultiExceptionHandler.test.cc index 3657ceddd3..a9b5a41603 100644 --- a/test/corecel/sys/MultiExceptionHandler.test.cc +++ b/test/corecel/sys/MultiExceptionHandler.test.cc @@ -41,9 +41,8 @@ TEST_F(MultiExceptionHandlerTest, single) { MultiExceptionHandler capture_exception; EXPECT_TRUE(capture_exception.empty()); - CELER_TRY_HANDLE( - throw RuntimeError::from_validate("first exception", "", "here", 1), - capture_exception); + CELER_TRY_HANDLE(CELER_RUNTIME_THROW("runtime", "first exception", ""), + capture_exception); EXPECT_FALSE(capture_exception.empty()); EXPECT_THROW(log_and_rethrow(std::move(capture_exception)), RuntimeError); @@ -52,14 +51,13 @@ TEST_F(MultiExceptionHandlerTest, single) TEST_F(MultiExceptionHandlerTest, multi) { MultiExceptionHandler capture_exception; - CELER_TRY_HANDLE( - throw RuntimeError::from_validate("first exception", "", "here", 1), - capture_exception); + CELER_TRY_HANDLE(CELER_RUNTIME_THROW("runtime", "first exception", ""), + capture_exception); for (auto i : range(3)) { DebugErrorDetails deets{ DebugErrorType::internal, "false", "test.cc", i}; - CELER_TRY_HANDLE(throw DebugError(deets), capture_exception); + CELER_TRY_HANDLE(throw DebugError(std::move(deets)), capture_exception); } EXPECT_THROW(log_and_rethrow(std::move(capture_exception)), RuntimeError); @@ -81,12 +79,13 @@ TEST_F(MultiExceptionHandlerTest, multi_nested) { MultiExceptionHandler capture_exception; CELER_TRY_HANDLE_CONTEXT( - throw RuntimeError::from_validate("first exception", "", "here", 1), + CELER_RUNTIME_THROW("runtime", "first exception", ""), capture_exception, MockContextException{}); DebugErrorDetails deets{DebugErrorType::internal, "false", "test.cc", 2}; - CELER_TRY_HANDLE_CONTEXT( - throw DebugError(deets), capture_exception, MockContextException{}); + CELER_TRY_HANDLE_CONTEXT(throw DebugError(std::move(deets)), + capture_exception, + MockContextException{}); EXPECT_THROW(log_and_rethrow(std::move(capture_exception)), MockContextException); diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index 6afaac0791..4a12df0e34 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -276,7 +276,7 @@ TEST_F(SolidConverterTest, sphere) {{-3, 0.05, 0}, {3, 0.5, 0}, {0, -0.01, 4.9}}); EXPECT_THROW( this->build_and_test(G4Sphere("sn12", 0, 50, 0, twopi, 0., 0.25 * pi)), - DebugError); + RuntimeError); this->build_and_test( G4Sphere("Spherical Shell", 45, 50, 0, twopi, 0, pi), @@ -285,7 +285,7 @@ TEST_F(SolidConverterTest, sphere) EXPECT_THROW( this->build_and_test(G4Sphere( "Band (theta segment1)", 45, 50, 0, twopi, pi * 3 / 4, pi / 4)), - DebugError); + RuntimeError); this->build_and_test( G4Sphere("Band (phi segment)", 5, 50, -pi, 3. * pi / 2., 0, twopi), @@ -293,7 +293,7 @@ TEST_F(SolidConverterTest, sphere) EXPECT_THROW( this->build_and_test(G4Sphere( "Patch (phi/theta seg)", 45, 50, -pi / 4, halfpi, pi / 4, halfpi)), - DebugError); + RuntimeError); this->build_and_test( G4Sphere("John example", 300, 500, 0, 5.76, 0, pi), diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 7dc127d45f..2bbce11144 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -403,7 +403,7 @@ using GenTrapTest = ConvexRegionTest; TEST_F(GenTrapTest, construct) { - // validate contruction parameters + // Validate contruction parameters EXPECT_THROW(GenTrap(-3, {{-1, -1}, {-1, 1}, {1, 1}, {1, -1}}, {{-2, -2}, {-2, 2}, {2, 2}, {2, -2}}), @@ -417,12 +417,11 @@ TEST_F(GenTrapTest, construct) {{-2, -2}, {-2, 2}, {2, 2}, {2, -2}}), RuntimeError); // non-convex - // a general, non-planar GenTrap, with 'twisted' faces - not yet - // implemented + // General non-planar GenTrap with 'twisted' faces is not yet implemented EXPECT_THROW(GenTrap(3, {{-10, -10}, {-10, 10}, {10, 10}, {9, -11}}, {{-10, -10}, {-10, 10}, {10, 10}, {10, -10}}), - DebugError); + RuntimeError); } TEST_F(GenTrapTest, box_like) From b9d65d364ef92f725c907729b5014af019e521bb Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 23 Apr 2024 23:46:16 -0400 Subject: [PATCH 15/59] Add basic raytrace components (#1194) * Move files to geocel and rename RunArgs * Remove demo rasterizer for now * Add Image and test * Add image line view and test * Add raytracer and test * Use size and add num_lines accessor * Create a separate file for image interface * Revert to using const ref * Add typedef to imager interface * Support unit input for image * Disable test * Add missing IO from image input * Rename test images * Add documentation from review feedback * Add check and tests for invalid basis function * Add detailed description of basis vector construction --- app/demo-rasterizer/CMakeLists.txt | 27 +-- app/demo-rasterizer/ImageData.hh | 38 ---- app/demo-rasterizer/ImageIO.cc | 68 ------ app/demo-rasterizer/ImageStore.cc | 117 ---------- app/demo-rasterizer/ImageStore.hh | 83 ------- app/demo-rasterizer/ImageTrackView.hh | 101 --------- app/demo-rasterizer/RDemoKernel.cu | 130 ----------- app/demo-rasterizer/RDemoKernel.hh | 39 ---- app/demo-rasterizer/RDemoRunner.cc | 72 ------ app/demo-rasterizer/RDemoRunner.hh | 46 ---- app/demo-rasterizer/demo-rasterizer.cc | 17 +- src/geocel/CMakeLists.txt | 2 + src/geocel/rasterize/Image.cc | 128 +++++++++++ src/geocel/rasterize/Image.hh | 134 +++++++++++ src/geocel/rasterize/ImageData.hh | 134 +++++++++++ src/geocel/rasterize/ImageIO.json.cc | 90 ++++++++ .../geocel/rasterize/ImageIO.json.hh | 28 +-- src/geocel/rasterize/ImageInterface.hh | 77 +++++++ src/geocel/rasterize/ImageLineView.hh | 112 ++++++++++ src/geocel/rasterize/Raytracer.hh | 182 +++++++++++++++ test/geocel/CMakeLists.txt | 11 + test/geocel/MockGeoTrackView.hh | 208 ++++++++++++++++++ test/geocel/rasterize/Image.test.cc | 117 ++++++++++ test/geocel/rasterize/Raytracer.test.cc | 143 ++++++++++++ 24 files changed, 1356 insertions(+), 748 deletions(-) delete mode 100644 app/demo-rasterizer/ImageData.hh delete mode 100644 app/demo-rasterizer/ImageIO.cc delete mode 100644 app/demo-rasterizer/ImageStore.cc delete mode 100644 app/demo-rasterizer/ImageStore.hh delete mode 100644 app/demo-rasterizer/ImageTrackView.hh delete mode 100644 app/demo-rasterizer/RDemoKernel.cu delete mode 100644 app/demo-rasterizer/RDemoKernel.hh delete mode 100644 app/demo-rasterizer/RDemoRunner.cc delete mode 100644 app/demo-rasterizer/RDemoRunner.hh create mode 100644 src/geocel/rasterize/Image.cc create mode 100644 src/geocel/rasterize/Image.hh create mode 100644 src/geocel/rasterize/ImageData.hh create mode 100644 src/geocel/rasterize/ImageIO.json.cc rename app/demo-rasterizer/ImageIO.hh => src/geocel/rasterize/ImageIO.json.hh (54%) create mode 100644 src/geocel/rasterize/ImageInterface.hh create mode 100644 src/geocel/rasterize/ImageLineView.hh create mode 100644 src/geocel/rasterize/Raytracer.hh create mode 100644 test/geocel/MockGeoTrackView.hh create mode 100644 test/geocel/rasterize/Image.test.cc create mode 100644 test/geocel/rasterize/Raytracer.test.cc diff --git a/app/demo-rasterizer/CMakeLists.txt b/app/demo-rasterizer/CMakeLists.txt index 8f1fe29d27..55d4d13191 100644 --- a/app/demo-rasterizer/CMakeLists.txt +++ b/app/demo-rasterizer/CMakeLists.txt @@ -6,27 +6,16 @@ set(SOURCES demo-rasterizer.cc - RDemoRunner.cc - ImageIO.cc - ImageStore.cc ) -if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP) - list(APPEND SOURCES - RDemoKernel.cu - ) - if(CELERITAS_USE_HIP) - set_source_files_properties( - RDemoKernel.cu - PROPERTIES LANGUAGE HIP - ) - endif() -endif() set(_rasterizer_libs - Celeritas::celeritas + Celeritas::corecel + Celeritas::geocel nlohmann_json::nlohmann_json ) -if(CELERITAS_CORE_GEO STREQUAL "VecGeom") +if(CELERITAS_CORE_GEO STREQUAL "ORANGE") + list(APPEND _rasterizer_libs Celeritas::orange) +elseif(CELERITAS_CORE_GEO STREQUAL "VecGeom") list(APPEND _rasterizer_libs VecGeom::vecgeom) endif() @@ -47,10 +36,8 @@ add_test(NAME "app/demo-rasterizer" COMMAND "$" "${_driver}" "${_gdml_inp}" ) -if((CELERITAS_CORE_GEO STREQUAL "ORANGE" AND NOT CELERITAS_USE_Geant4) - OR CELER_DISABLE_DEVICE) - # two-boxes.org.json isn't set up in the test directory - # or device is unavailable +if(TRUE) + # demo rasterizer is disabled until the next PR set(_maybe_disable DISABLED true) else() set(_maybe_disable) diff --git a/app/demo-rasterizer/ImageData.hh b/app/demo-rasterizer/ImageData.hh deleted file mode 100644 index 858eba6d53..0000000000 --- a/app/demo-rasterizer/ImageData.hh +++ /dev/null @@ -1,38 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/ImageData.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "corecel/Types.hh" -#include "corecel/cont/Span.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// -/*! - * Construction arguments for an on-device image view. - */ -struct ImageData -{ - Real3 origin; //!< Upper left corner - Real3 down_ax; //!< Downward axis (increasing j, track - //!< initialization) - Real3 right_ax; //!< Rightward axis (increasing i, track - //!< movement) - real_type pixel_width; //!< Width of a pixel - Array dims; //!< Image dimensions (j, i) - Span image; //!< Stored image [j][i] - - //! Whether the interface is initialized - explicit CELER_FUNCTION operator bool() const { return !image.empty(); } -}; - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/ImageIO.cc b/app/demo-rasterizer/ImageIO.cc deleted file mode 100644 index 25e4b5eddb..0000000000 --- a/app/demo-rasterizer/ImageIO.cc +++ /dev/null @@ -1,68 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/ImageIO.cc -//---------------------------------------------------------------------------// -#include "ImageIO.hh" - -#include "celeritas_cmake_strings.h" -#include "corecel/cont/ArrayIO.json.hh" -#include "corecel/math/ArrayOperators.hh" -#include "celeritas/Quantities.hh" -#include "celeritas/Types.hh" - -#include "ImageStore.hh" - -namespace celeritas -{ -namespace app -{ -namespace -{ -//---------------------------------------------------------------------------// -Real3 from_cm(Real3 const& r) -{ - using CmPoint = Quantity; - return native_value_from(CmPoint{r}); -} - -//---------------------------------------------------------------------------// -} // namespace - -//---------------------------------------------------------------------------// -//!@{ -//! I/O routines for JSON -void to_json(nlohmann::json& j, ImageRunArgs const& v) -{ - j = nlohmann::json{{"lower_left", v.lower_left}, - {"upper_right", v.upper_right}, - {"rightward_ax", v.rightward_ax}, - {"vertical_pixels", v.vertical_pixels}, - {"_units", celeritas_units}}; -} - -void from_json(nlohmann::json const& j, ImageRunArgs& v) -{ - v.lower_left = from_cm(j.at("lower_left").get()); - v.upper_right = from_cm(j.at("upper_right").get()); - j.at("rightward_ax").get_to(v.rightward_ax); - j.at("vertical_pixels").get_to(v.vertical_pixels); -} - -void to_json(nlohmann::json& j, ImageStore const& v) -{ - j = nlohmann::json{{"origin", v.origin()}, - {"down_ax", v.down_ax()}, - {"right_ax", v.right_ax()}, - {"pixel_width", v.pixel_width()}, - {"dims", v.dims()}, - {"int_size", sizeof(int)}, - {"_units", celeritas_units}}; -} - -//!@} -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/ImageStore.cc b/app/demo-rasterizer/ImageStore.cc deleted file mode 100644 index 985f5d24b1..0000000000 --- a/app/demo-rasterizer/ImageStore.cc +++ /dev/null @@ -1,117 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/ImageStore.cc -//---------------------------------------------------------------------------// -#include "ImageStore.hh" - -#include "corecel/cont/Range.hh" -#include "corecel/math/ArrayUtils.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// -/*! - * Construct with image slice and extents. - */ -ImageStore::ImageStore(ImageRunArgs params) -{ - CELER_EXPECT(is_soft_unit_vector(params.rightward_ax)); - CELER_EXPECT(params.lower_left != params.upper_right); - CELER_EXPECT(params.vertical_pixels > 0); - - // Normalize rightward axis - right_ax_ = make_unit_vector(params.rightward_ax); - - // Vector pointing toward the upper right from the lower left corner - Real3 diagonal; - for (int i : range(3)) - { - diagonal[i] = params.upper_right[i] - params.lower_left[i]; - } - - // Set downward axis to the diagonal with the rightward component - // subtracted out; then normalize - real_type projection = dot_product(diagonal, right_ax_); - for (int i : range(3)) - { - down_ax_[i] = -diagonal[i] + projection * right_ax_[i]; - } - down_ax_ = make_unit_vector(down_ax_); - - // Calculate length along each axis - real_type width_x = dot_product(diagonal, right_ax_); - real_type width_y = -dot_product(diagonal, down_ax_); - - CELER_ASSERT(width_x > 0 && width_y > 0); - - // Set number of pixels in each direction. - auto num_y = params.vertical_pixels; - pixel_width_ = width_y / num_y; - auto num_x = std::max(std::ceil(width_x / pixel_width_), 1); - - // Set upper left corner - for (int i : range(3)) - { - origin_[i] = params.lower_left[i] - down_ax_[i] * num_y * pixel_width_; - } - - // Allocate storage - dims_ = {num_y, num_x}; - image_ = DeviceVector(num_y * num_x); - CELER_ENSURE(!image_.empty()); -} - -//---------------------------------------------------------------------------// -/*! - * Access image on host for initializing. - */ -ImageData ImageStore::host_interface() -{ - ImageData result; - - result.origin = origin_; - result.down_ax = down_ax_; - result.right_ax = right_ax_; - result.pixel_width = pixel_width_; - result.dims = dims_; - - return result; -} - -//---------------------------------------------------------------------------// -/*! - * Access image on device for writing. - */ -ImageData ImageStore::device_interface() -{ - ImageData result; - - result.origin = origin_; - result.down_ax = down_ax_; - result.right_ax = right_ax_; - result.pixel_width = pixel_width_; - result.dims = dims_; - result.image = image_.device_ref(); - - return result; -} - -//---------------------------------------------------------------------------// -/*! - * Copy out the image to the host. - */ -auto ImageStore::data_to_host() const -> VecInt -{ - VecInt result(dims_[0] * dims_[1]); - image_.copy_to_host(make_span(result)); - return result; -} - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/ImageStore.hh b/app/demo-rasterizer/ImageStore.hh deleted file mode 100644 index c822843ca4..0000000000 --- a/app/demo-rasterizer/ImageStore.hh +++ /dev/null @@ -1,83 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/ImageStore.hh -//---------------------------------------------------------------------------// -#pragma once - -#include - -#include "corecel/Types.hh" -#include "corecel/cont/Array.hh" -#include "corecel/cont/Span.hh" -#include "corecel/data/DeviceVector.hh" -#include "celeritas/Types.hh" - -#include "ImageData.hh" -#include "ImageIO.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// -/*! - * Initialization and storage for a raster image. - */ -class ImageStore -{ - public: - //!@{ - //! \name Type aliases - - using UInt2 = Array; - - using VecInt = std::vector; - //!@} - - public: - // Construct with defaults - explicit ImageStore(ImageRunArgs); - - //// DEVICE ACCESSORS //// - - //! Access image on host for initializing - ImageData host_interface(); - - //! Access image on device for writing - ImageData device_interface(); - - //// HOST ACCESSORS //// - - //! Upper left corner of the image - Real3 const& origin() const { return origin_; } - - //! Downward axis (increasing j) - Real3 const& down_ax() const { return down_ax_; } - - //! Rightward axis (increasing i) - Real3 const& right_ax() const { return right_ax_; } - - //! Width of a pixel - real_type pixel_width() const { return pixel_width_; } - - //! Dimensions {j, i} of the image - UInt2 const& dims() const { return dims_; } - - // Copy out the image to the host - VecInt data_to_host() const; - - private: - Real3 origin_; - Real3 down_ax_; - Real3 right_ax_; - real_type pixel_width_; - UInt2 dims_; - DeviceVector image_; -}; - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/ImageTrackView.hh b/app/demo-rasterizer/ImageTrackView.hh deleted file mode 100644 index 8c2eff9e47..0000000000 --- a/app/demo-rasterizer/ImageTrackView.hh +++ /dev/null @@ -1,101 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/ImageTrackView.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "corecel/Assert.hh" - -#include "ImageData.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// -/*! - * Modify a line of a rasterized image. - * - * The rasterizer starts at the left side of an image and traces rightward. - * Each "line" is a single thread. - */ -class ImageTrackView -{ - public: - //!@{ - //! \name Type aliases - - //!@} - - public: - // Construct with image data and thread ID - inline CELER_FUNCTION - ImageTrackView(ImageData const& shared, TrackSlotId tid); - - // Calculate start position - inline CELER_FUNCTION Real3 start_pos() const; - - //! Start direction (rightward axis) - CELER_FUNCTION Real3 const& start_dir() const { return shared_.right_ax; } - - //! Pixel width - CELER_FUNCTION real_type pixel_width() const - { - return shared_.pixel_width; - } - - // Set pixel value - inline CELER_FUNCTION void set_pixel(unsigned int i, int value); - - private: - ImageData const& shared_; - unsigned int j_index_; -}; - -//---------------------------------------------------------------------------// -// INLINE DEFINITIONS -//---------------------------------------------------------------------------// -/*! - * Construct with defaults. - */ -CELER_FUNCTION -ImageTrackView::ImageTrackView(ImageData const& shared, TrackSlotId tid) - : shared_(shared), j_index_(tid.get()) -{ - CELER_EXPECT(j_index_ < shared_.dims[0]); -} - -//---------------------------------------------------------------------------// -/*! - * Calculate starting position. - */ -CELER_FUNCTION auto ImageTrackView::start_pos() const -> Real3 -{ - Real3 result; - real_type down_offset = (j_index_ + real_type(0.5)) * shared_.pixel_width; - for (int i = 0; i < 3; ++i) - { - result[i] = shared_.origin[i] + shared_.down_ax[i] * down_offset; - } - return result; -} - -//---------------------------------------------------------------------------// -/*! - * Set the value for a pixel. - */ -CELER_FUNCTION void ImageTrackView::set_pixel(unsigned int i, int value) -{ - CELER_EXPECT(i < shared_.dims[1]); - unsigned int idx = j_index_ * shared_.dims[1] + i; - - CELER_ASSERT(idx < shared_.image.size()); - shared_.image[idx] = value; -} - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/RDemoKernel.cu b/app/demo-rasterizer/RDemoKernel.cu deleted file mode 100644 index 2bc38ab74a..0000000000 --- a/app/demo-rasterizer/RDemoKernel.cu +++ /dev/null @@ -1,130 +0,0 @@ -//---------------------------------*-CUDA-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/RDemoKernel.cu -//---------------------------------------------------------------------------// -#include "RDemoKernel.hh" - -#include - -#include "corecel/Assert.hh" -#include "corecel/sys/KernelParamCalculator.device.hh" -#include "celeritas/geo/GeoTrackView.hh" - -#include "ImageTrackView.hh" - -namespace celeritas -{ -namespace app -{ -namespace -{ -//---------------------------------------------------------------------------// -// KERNELS -//---------------------------------------------------------------------------// - -__device__ int geo_id(GeoTrackView const& geo) -{ - if (geo.is_outside()) - return -1; - return geo.volume_id().get(); -} - -__global__ void trace_kernel(GeoParamsCRefDevice const geo_params, - GeoStateRefDevice const geo_state, - ImageData const image_state) -{ - auto tid = KernelParamCalculator::thread_id(); - if (tid.get() >= image_state.dims[0]) - return; - - TrackSlotId tsid{tid.unchecked_get()}; - ImageTrackView image(image_state, tsid); - GeoTrackView geo(geo_params, geo_state, tsid); - - // Start track at the leftmost point in the requested direction - geo = GeoTrackInitializer{image.start_pos(), image.start_dir()}; - - int cur_id = geo_id(geo); - - // Track along each pixel - for (unsigned int i = 0; i < image_state.dims[1]; ++i) - { - real_type pix_dist = image_state.pixel_width; - real_type max_dist = 0; - int max_id = cur_id; - int abort_counter = 32; // max number of crossings per pixel - - auto next = geo.find_next_step(pix_dist); - while (next.boundary && pix_dist > 0) - { - CELER_ASSERT(next.distance <= pix_dist); - // Move to geometry boundary - pix_dist -= next.distance; - - if (max_id == cur_id) - { - max_dist += next.distance; - } - else if (next.distance > max_dist) - { - max_dist = next.distance; - max_id = cur_id; - } - - // Cross surface and update post-crossing ID - geo.move_to_boundary(); - geo.cross_boundary(); - cur_id = geo_id(geo); - - if (--abort_counter == 0) - { - // Reinitialize at end of pixel - Real3 new_pos = image.start_pos(); - axpy((i + 1) * image_state.pixel_width, - image.start_dir(), - &new_pos); - geo = GeoTrackInitializer{new_pos, image.start_dir()}; - pix_dist = 0; - } - if (pix_dist > 0) - { - // Next movement is to end of geo or pixel - next = geo.find_next_step(pix_dist); - } - } - - if (pix_dist > 0) - { - // Move to pixel boundary - geo.move_internal(pix_dist); - if (pix_dist > max_dist) - { - max_dist = pix_dist; - max_id = cur_id; - } - } - image.set_pixel(i, max_id); - } -} -} // namespace - -//---------------------------------------------------------------------------// -// KERNEL INTERFACE -//---------------------------------------------------------------------------// -void trace(GeoParamsCRefDevice const& geo_params, - GeoStateRefDevice const& geo_state, - ImageData const& image) -{ - CELER_EXPECT(image); - - CELER_LAUNCH_KERNEL(trace, image.dims[0], 0, geo_params, geo_state, image); - - CELER_DEVICE_CALL_PREFIX(DeviceSynchronize()); -} - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/RDemoKernel.hh b/app/demo-rasterizer/RDemoKernel.hh deleted file mode 100644 index 49a51cca79..0000000000 --- a/app/demo-rasterizer/RDemoKernel.hh +++ /dev/null @@ -1,39 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/RDemoKernel.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "corecel/Assert.hh" -#include "corecel/Macros.hh" -#include "celeritas/geo/GeoData.hh" - -#include "ImageData.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// - -using GeoParamsCRefDevice = DeviceCRef; -using GeoStateRefDevice = DeviceRef; - -void trace(GeoParamsCRefDevice const& geo_params, - GeoStateRefDevice const& geo_state, - ImageData const& image); - -#if !CELER_USE_DEVICE -inline void -trace(GeoParamsCRefDevice const&, GeoStateRefDevice const&, ImageData const&) -{ - CELER_NOT_CONFIGURED("CUDA or HIP"); -} -#endif - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/RDemoRunner.cc b/app/demo-rasterizer/RDemoRunner.cc deleted file mode 100644 index d4fad8e930..0000000000 --- a/app/demo-rasterizer/RDemoRunner.cc +++ /dev/null @@ -1,72 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/RDemoRunner.cc -//---------------------------------------------------------------------------// -#include "RDemoRunner.hh" - -#include "corecel/cont/Range.hh" -#include "corecel/data/CollectionStateStore.hh" -#include "corecel/io/ColorUtils.hh" -#include "corecel/io/Logger.hh" -#include "corecel/sys/Stopwatch.hh" -#include "celeritas/geo/GeoParams.hh" - -#include "ImageTrackView.hh" -#include "RDemoKernel.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// -/*! - * Construct with image parameters - */ -RDemoRunner::RDemoRunner(SPConstGeo geometry) - : geo_params_(std::move(geometry)) -{ - CELER_EXPECT(geo_params_); -} - -//---------------------------------------------------------------------------// -/*! - * Trace an image. - */ -void RDemoRunner::operator()(ImageStore* image, int ntimes) const -{ - CELER_EXPECT(image); - - CollectionStateStore geo_state( - geo_params_->host_ref(), image->dims()[0]); - - CELER_LOG(status) << "Tracing geometry"; - // do it ntimes+1 as first one tends to be a warm-up run (slightly longer) - double sum = 0, time = 0; - for (int i = 0; i <= ntimes; ++i) - { - Stopwatch get_time; - trace(geo_params_->device_ref(), - geo_state.ref(), - image->device_interface()); - time = get_time(); - CELER_LOG(info) << color_code('x') << "Elapsed " << i << ": " << time - << " s" << color_code(' '); - if (i > 0) - { - sum += time; - } - } - if (ntimes > 0) - { - CELER_LOG(info) << color_code('x') - << "\tAverage time: " << sum / ntimes << " s" - << color_code(' '); - } -} - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/RDemoRunner.hh b/app/demo-rasterizer/RDemoRunner.hh deleted file mode 100644 index d7b78b5b56..0000000000 --- a/app/demo-rasterizer/RDemoRunner.hh +++ /dev/null @@ -1,46 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/RDemoRunner.hh -//---------------------------------------------------------------------------// -#pragma once - -#include - -#include "celeritas/geo/GeoParams.hh" - -#include "ImageStore.hh" - -namespace celeritas -{ -namespace app -{ -//---------------------------------------------------------------------------// -/*! - * Set up and run rasterization of the given device image. - */ -class RDemoRunner -{ - public: - //!@{ - //! \name Type aliases - using SPConstGeo = std::shared_ptr; - using Args = ImageRunArgs; - //!@} - - public: - // Construct with geometry - explicit RDemoRunner(SPConstGeo geometry); - - // Trace an image - void operator()(ImageStore* image, int ntimes = 0) const; - - private: - SPConstGeo geo_params_; -}; - -//---------------------------------------------------------------------------// -} // namespace app -} // namespace celeritas diff --git a/app/demo-rasterizer/demo-rasterizer.cc b/app/demo-rasterizer/demo-rasterizer.cc index 1b1c7d6943..dc5a78353a 100644 --- a/app/demo-rasterizer/demo-rasterizer.cc +++ b/app/demo-rasterizer/demo-rasterizer.cc @@ -14,7 +14,6 @@ #include "celeritas_version.h" #include "corecel/cont/Range.hh" -#include "corecel/io/ColorUtils.hh" #include "corecel/io/Logger.hh" #include "corecel/sys/Device.hh" #include "corecel/sys/DeviceIO.json.hh" @@ -23,8 +22,7 @@ #include "corecel/sys/MpiCommunicator.hh" #include "corecel/sys/ScopedMpiInit.hh" #include "corecel/sys/Stopwatch.hh" - -#include "RDemoRunner.hh" +#include "celeritas/geo/GeoParams.hh" namespace celeritas { @@ -58,14 +56,7 @@ void run(std::istream& is) inp.at("input").get().c_str()); timers["load"] = get_time(); - // Construct image - ImageStore image(inp.at("image").get()); - - // Construct runner - RDemoRunner run(geo_params); - get_time = {}; - run(&image); - timers["trace"] = get_time(); + CELER_NOT_IMPLEMENTED("demo-rasterizer"); // Get geometry names std::vector vol_names; @@ -78,7 +69,7 @@ void run(std::istream& is) CELER_LOG(status) << "Transferring image from GPU to disk"; get_time = {}; std::string out_filename = inp.at("output"); - auto image_data = image.data_to_host(); + std::vector image_data; std::ofstream(out_filename, std::ios::binary) .write(reinterpret_cast(image_data.data()), image_data.size() * sizeof(decltype(image_data)::value_type)); @@ -87,7 +78,7 @@ void run(std::istream& is) // Construct json output CELER_LOG(status) << "Exporting JSON metadata"; nlohmann::json outp = { - {"metadata", image}, + {"metadata", nullptr}, {"data", out_filename}, {"volumes", vol_names}, {"timers", timers}, diff --git a/src/geocel/CMakeLists.txt b/src/geocel/CMakeLists.txt index 3948989988..ac69342657 100644 --- a/src/geocel/CMakeLists.txt +++ b/src/geocel/CMakeLists.txt @@ -13,6 +13,7 @@ set(PUBLIC_DEPS Celeritas::corecel) list(APPEND SOURCES GeoParamsOutput.cc + rasterize/Image.cc ) #-----------------------------------------------------------------------------# @@ -22,6 +23,7 @@ list(APPEND SOURCES if(CELERITAS_USE_JSON) list(APPEND SOURCES BoundingBoxIO.json.cc + rasterize/ImageIO.json.cc ) list(APPEND PRIVATE_DEPS nlohmann_json::nlohmann_json) endif() diff --git a/src/geocel/rasterize/Image.cc b/src/geocel/rasterize/Image.cc new file mode 100644 index 0000000000..15902f2db0 --- /dev/null +++ b/src/geocel/rasterize/Image.cc @@ -0,0 +1,128 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/Image.cc +//---------------------------------------------------------------------------// +#include "Image.hh" + +#include + +#include "corecel/cont/ArrayIO.hh" +#include "corecel/cont/Range.hh" +#include "corecel/data/CollectionAlgorithms.hh" +#include "corecel/io/Repr.hh" +#include "corecel/math/ArrayOperators.hh" +#include "corecel/math/ArraySoftUnit.hh" +#include "corecel/math/ArrayUtils.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct with image properties. + * + * All inputs should be in the native unit system. This constructor uses the + * two user-provided points along with the basis vector to determine the new + * "origin" (upper-left corner) and the window's basis functions. + */ +ImageParams::ImageParams(ImageInput const& inp) +{ + CELER_VALIDATE(ArraySoftUnit{real_type{0.001}}(inp.rightward), + << "rightward axis " << repr(inp.rightward) + << " is not a unit vector"); + CELER_VALIDATE(!(inp.lower_left == inp.upper_right), + << "lower left corner " << repr(inp.lower_left) + << " and upper right corner cannot be the same"); + CELER_VALIDATE(inp.vertical_pixels > 0, + << "number of pixels must be positive"); + CELER_VALIDATE(inp.horizontal_divisor > 0, + << "number of horizontal chunks must be positive"); + + ImageParamsScalars scalars; + + // Vector pointing toward the upper right from the lower left corner + Real3 diagonal = inp.upper_right - inp.lower_left; + + /*! + * Construct orthonormal basis functions using the rightward vector and + * user-supplied window. + * + * 1. Normalize rightward vector. + * 2. Project the image diagonal onto the rightward vector and subtract + * that component from the diagonal to orthogonalize it. + * 3. Flip the resulting "upward" vector to become a downward direction. + * 4. Normalize the downward basis vector. + */ + scalars.right = make_unit_vector(inp.rightward); + real_type projection = dot_product(diagonal, scalars.right); + CELER_VALIDATE(projection > 0, + << "rightward direction is incompatible with image window"); + scalars.down = -diagonal; + axpy(projection, scalars.right, &scalars.down); + scalars.down = make_unit_vector(scalars.down); + + // Calculate length along each axis + real_type width_x = dot_product(diagonal, scalars.right); + real_type width_y = -dot_product(diagonal, scalars.down); + CELER_ASSERT(width_x > 0 && width_y > 0); + scalars.max_length = width_x; + + // Set number of pixels in each direction. + size_type num_y = inp.vertical_pixels; + scalars.pixel_width = width_y / num_y; + size_type num_x + = inp.horizontal_divisor + * std::ceil(width_x / (inp.horizontal_divisor * scalars.pixel_width)); + CELER_ASSERT(num_x >= inp.horizontal_divisor); + scalars.dims = {num_y, num_x}; + + // Set upper left corner + scalars.origin = inp.lower_left; + axpy(-(num_y * scalars.pixel_width), scalars.down, &scalars.origin); + + // Allocate storage and "copy" to device + CELER_ASSERT(scalars); + data_ + = CollectionMirror{HostVal{scalars}}; + CELER_ENSURE(data_); +} + +//---------------------------------------------------------------------------// +/*! + * Construct from parameters. + */ +template +Image::Image(SPConstParams params) : params_(std::move(params)) +{ + CELER_EXPECT(params_); + + // Allocate the image, save a reference, and fill with "invalid" + resize(&value_, params_->host_ref()); + ref_ = value_; + celeritas::fill(-1, &ref_.image); +} + +//---------------------------------------------------------------------------// +/*! + * Copy the image back to the host. + */ +template +void Image::copy_to_host(SpanInt out) const +{ + CELER_VALIDATE(out.size() == ref_.image.size(), + << "invalid output size " << out.size() + << " for image copying: should be " << ref_.image.size()); + celeritas::copy_to_host(ref_.image, out); +} + +//---------------------------------------------------------------------------// +// EXPLICIT INSTANTIATION +//---------------------------------------------------------------------------// + +template class Image; +template class Image; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/Image.hh b/src/geocel/rasterize/Image.hh new file mode 100644 index 0000000000..23ae735d09 --- /dev/null +++ b/src/geocel/rasterize/Image.hh @@ -0,0 +1,134 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/Image.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Types.hh" +#include "corecel/data/CollectionMirror.hh" +#include "corecel/data/ParamsDataInterface.hh" + +#include "ImageData.hh" +#include "ImageInterface.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Image construction arguments. + * + * Image scale in this struct is *native* units, but JSON I/O defaults to + * centimeters for the window coordinates and accepts an optional "_units" + * parameter that can take values of cgs, si, or clhep to interpret the input + * as centimeters, meters, or millimeters. + */ +struct ImageInput +{ + //!@{ + //! Coordinates of the window [length] + Real3 lower_left{0, 0, 0}; + Real3 upper_right{}; + //!@} + + //! Rightward basis vector, the new "x" axis + Real3 rightward{1, 0, 0}; + + //! Number of vertical pixels, aka threads when raytracing + size_type vertical_pixels{}; + + //! Round the number of horizontal pixels to this value + size_type horizontal_divisor{CELER_USE_DEVICE ? 128 / sizeof(int) : 1}; + + //! True if the input is unassigned + explicit operator bool() const + { + return vertical_pixels != 0 && lower_left != upper_right; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Manage properties of an image. + * + * An image is a "window", a 2D rectangle slice of 3D space. As with computer + * GUI windows, matplotlib \c imshow, and other visual rendering layouts, the + * pixel order is like text on a page: left to right, then top to bottom. + * Because this is vertically flipped from "mathematical" ordering, we store + * the upper left coordinate and a \em -y basis vector rather than a lower left + * coordinate and a \em +y basis vector. + * + * The same image params can be used to construct multiple images (using + * different ray tracing methods or different geometries or on host vs device). + */ +class ImageParams final : public ParamsDataInterface +{ + public: + // Construct with image properties + explicit ImageParams(ImageInput const&); + + //! Access scalar image properties + ImageParamsScalars const& scalars() const + { + return this->host_ref().scalars; + } + + //! Number of pixels in an image created from these params + size_type num_pixels() const + { + auto const& dims = this->scalars().dims; + return dims[0] * dims[1]; + } + + //! Number of horizontal lines to be used for raytracing + size_type num_lines() const { return this->scalars().dims[0]; } + + //! Access properties on the host + HostRef const& host_ref() const final { return data_.host_ref(); } + + //! Access properties on the device + DeviceRef const& device_ref() const final { return data_.device_ref(); } + + private: + CollectionMirror data_; +}; + +//---------------------------------------------------------------------------// +/*! + * Implement an image on host or device. + */ +template +class Image final : public ImageInterface +{ + public: + //!@{ + //! \name Type aliases + using Value = ImageStateData; + using Ref = ImageStateData; + //!@} + + public: + // Construct from parameters + explicit Image(SPConstParams params); + + //! Access image properties + SPConstParams const& params() const final { return params_; } + + // Write the image to a stream in binary format + void copy_to_host(SpanInt) const final; + + //! Access the mutable state data + Ref const& ref() { return ref_; } + + private: + SPConstParams params_; + Value value_; + Ref ref_; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/ImageData.hh b/src/geocel/rasterize/ImageData.hh new file mode 100644 index 0000000000..13abe712c8 --- /dev/null +++ b/src/geocel/rasterize/ImageData.hh @@ -0,0 +1,134 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/ImageData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "corecel/cont/Array.hh" +#include "corecel/data/Collection.hh" +#include "corecel/data/CollectionBuilder.hh" +#include "geocel/Types.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Scalar properties for building a rasterized image. + * + * These properties specify a "window" that's a slice of a 3D geometry. It uses + * graphics conventions of making the upper left corner the origin. + * + * The \c down basis vector corresponds to increasing \em j and is used for + * track initialization. The \c right basis vector corresponds to increasing + * \em i and is used for track movement. Because the user-specified window may + * not have an integer ratio of the two sides, we have a "max length" for + * raytracing to the right. This also lets us round up the image dimensions to + * a convenient alignment. + * + * All units are "native" length. + */ +struct ImageParamsScalars +{ + using Size2 = Array; + + Real3 origin{}; //!< Upper left corner + Real3 down{}; //!< Downward basis vector + Real3 right{}; //!< Rightward basis vector (increasing i, track movement) + real_type pixel_width{}; //!< Width of a pixel + Size2 dims{}; //!< Image dimensions (rows, columns) + real_type max_length{}; //!< Maximum distance along rightward to trace + + //! Whether the interface is initialized + explicit CELER_FUNCTION operator bool() const + { + return pixel_width > 0 && dims[0] > 0 && dims[1] > 0 && max_length > 0; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Persistent data used to construct an image. + * + * TODO: add material/cell -> RGB for inline rendering? + */ +template +struct ImageParamsData +{ + //// DATA //// + + ImageParamsScalars scalars; + + //// METHODS //// + + //! True if assigned + explicit CELER_FUNCTION operator bool() const + { + return static_cast(scalars); + } + + //! Assign from another set of data + template + ImageParamsData& operator=(ImageParamsData const& other) + { + CELER_EXPECT(other); + scalars = other.scalars; + return *this; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Image state data. + * + * This is just a representation of the image itself. + */ +template +struct ImageStateData +{ + //// TYPES //// + + template + using Items = celeritas::Collection; + + //// DATA //// + + Items image; //!< Stored image [i][j] + + //// METHODS //// + + //! True if sizes are consistent and nonzero + explicit CELER_FUNCTION operator bool() const { return !image.empty(); } + + //! Number of pixels + CELER_FUNCTION size_type size() const { return image.size(); } + + //! Assign from another set of data + template + ImageStateData& operator=(ImageStateData& other) + { + CELER_EXPECT(other); + image = other.image; + return *this; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Resize geometry tracking states. + */ +template +inline void resize(ImageStateData* data, + HostCRef const& params) +{ + CELER_EXPECT(data); + CELER_EXPECT(params); + + resize(&data->image, params.scalars.dims[0] * params.scalars.dims[1]); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/ImageIO.json.cc b/src/geocel/rasterize/ImageIO.json.cc new file mode 100644 index 0000000000..40cfd08aed --- /dev/null +++ b/src/geocel/rasterize/ImageIO.json.cc @@ -0,0 +1,90 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/ImageIO.json.cc +//---------------------------------------------------------------------------// +#include "ImageIO.json.hh" + +#include "corecel/Assert.hh" +#include "corecel/Types.hh" +#include "corecel/cont/Array.hh" +#include "corecel/cont/ArrayIO.json.hh" +#include "corecel/io/JsonUtils.json.hh" +#include "corecel/math/ArrayOperators.hh" +#include "geocel/detail/LengthUnits.hh" + +#include "Image.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +//!@{ +//! I/O routines for JSON +#define IM_LOAD_OPTION(NAME) CELER_JSON_LOAD_OPTION(j, v, NAME) +#define IM_LOAD_REQUIRED(NAME) CELER_JSON_LOAD_REQUIRED(j, v, NAME) + +void from_json(nlohmann::json const& j, ImageInput& v) +{ + IM_LOAD_REQUIRED(lower_left); + IM_LOAD_REQUIRED(upper_right); + IM_LOAD_REQUIRED(rightward); + IM_LOAD_REQUIRED(vertical_pixels); + IM_LOAD_OPTION(horizontal_divisor); + + real_type length{lengthunits::centimeter}; + if (auto iter = j.find("_units"); iter != j.end()) + { + switch (to_unit_system(iter->get())) + { + case UnitSystem::cgs: + length = lengthunits::centimeter; + break; + case UnitSystem::si: + length = lengthunits::meter; + break; + case UnitSystem::clhep: + length = lengthunits::millimeter; + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + } + if (length != 1) + { + v.lower_left *= length; + v.upper_right *= length; + } +} + +void to_json(nlohmann::json& j, ImageInput const& v) +{ + j = nlohmann::json{ + CELER_JSON_PAIR(v, lower_left), + CELER_JSON_PAIR(v, upper_right), + CELER_JSON_PAIR(v, rightward), + CELER_JSON_PAIR(v, vertical_pixels), + CELER_JSON_PAIR(v, horizontal_divisor), + {"_units", to_cstring(UnitSystem::native)}, + }; +} + +void to_json(nlohmann::json& j, ImageParams const& p) +{ + auto const& scalars = p.host_ref().scalars; + j = nlohmann::json{ + CELER_JSON_PAIR(scalars, origin), + CELER_JSON_PAIR(scalars, down), + CELER_JSON_PAIR(scalars, right), + CELER_JSON_PAIR(scalars, pixel_width), + CELER_JSON_PAIR(scalars, dims), + {"_units", to_cstring(UnitSystem::native)}, + }; +} + +#undef IM_LOAD_OPTION +#undef IM_LOAD_REQUIRED +//!@} +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/app/demo-rasterizer/ImageIO.hh b/src/geocel/rasterize/ImageIO.json.hh similarity index 54% rename from app/demo-rasterizer/ImageIO.hh rename to src/geocel/rasterize/ImageIO.json.hh index 0d32612c43..bb85dba2bf 100644 --- a/app/demo-rasterizer/ImageIO.hh +++ b/src/geocel/rasterize/ImageIO.json.hh @@ -3,36 +3,22 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file demo-rasterizer/ImageIO.hh +//! \file geocel/rasterize/ImageIO.json.hh //---------------------------------------------------------------------------// #pragma once #include -#include "corecel/Assert.hh" -#include "corecel/Types.hh" -#include "corecel/cont/Array.hh" -#include "celeritas/Types.hh" - namespace celeritas { -namespace app -{ -class ImageStore; //---------------------------------------------------------------------------// -//! Image construction arguments -struct ImageRunArgs -{ - Real3 lower_left; - Real3 upper_right; - Real3 rightward_ax; - unsigned int vertical_pixels; -}; +struct ImageInput; +class ImageParams; -void to_json(nlohmann::json& j, ImageRunArgs const& value); -void from_json(nlohmann::json const& j, ImageRunArgs& value); +//---------------------------------------------------------------------------// +void to_json(nlohmann::json& j, ImageInput const&); +void from_json(nlohmann::json const& j, ImageInput&); -void to_json(nlohmann::json& j, ImageStore const& value); +void to_json(nlohmann::json& j, ImageParams const&); //---------------------------------------------------------------------------// -} // namespace app } // namespace celeritas diff --git a/src/geocel/rasterize/ImageInterface.hh b/src/geocel/rasterize/ImageInterface.hh new file mode 100644 index 0000000000..1eb44ff71b --- /dev/null +++ b/src/geocel/rasterize/ImageInterface.hh @@ -0,0 +1,77 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/ImageInterface.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "corecel/cont/Span.hh" + +namespace celeritas +{ +class ImageParams; + +//---------------------------------------------------------------------------// +/*! + * Access data from an image. + * + * Images currently are arrays of integer pixels. + */ +class ImageInterface +{ + public: + //!@{ + //! \name Type aliases + using int_type = int; + using SpanInt = Span; + using SPConstParams = std::shared_ptr; + //!@} + + public: + //! Default virtual destructor + virtual ~ImageInterface() = default; + + //! Access image properties + virtual SPConstParams const& params() const = 0; + + //! Copy the image to the host + virtual void copy_to_host(SpanInt) const = 0; + + protected: + ImageInterface() = default; + CELER_DEFAULT_COPY_MOVE(ImageInterface); +}; + +// Forward-declare image, see Image.hh +template +class Image; + +//---------------------------------------------------------------------------// +/*! + * Generate one or more images from a geometry. + */ +class ImagerInterface +{ + public: + //! Default virtual destructor + virtual ~ImagerInterface() = default; + + //!@{ + //! Raytrace an image on host or device + virtual void operator()(Image*) = 0; + virtual void operator()(Image*) = 0; + //!@} + + protected: + ImagerInterface() = default; + CELER_DEFAULT_COPY_MOVE(ImagerInterface); +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/ImageLineView.hh b/src/geocel/rasterize/ImageLineView.hh new file mode 100644 index 0000000000..649db925f1 --- /dev/null +++ b/src/geocel/rasterize/ImageLineView.hh @@ -0,0 +1,112 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/ImageLineView.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "corecel/math/ArrayUtils.hh" +#include "geocel/Types.hh" + +#include "ImageData.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Modify a line of a image for rasterization. + * + * The rasterizer starts at the left side of an image and traces rightward. + * Each "line" is a single thread. + * + * \todo Add template parameter to switch from vertical to horizontal + * direction. + */ +class ImageLineView +{ + public: + //!@{ + //! \name Type aliases + using ParamsRef = NativeCRef; + using StateRef = NativeRef; + //!@} + + public: + // Construct with image data and thread ID + inline CELER_FUNCTION ImageLineView(ParamsRef const& params, + StateRef const& state, + size_type row_index); + + // Calculate start position + inline CELER_FUNCTION Real3 start_pos() const; + + //! Start direction (rightward axis) + CELER_FUNCTION Real3 const& start_dir() const { return scalars_.right; } + + //! Pixel width + CELER_FUNCTION real_type pixel_width() const + { + return scalars_.pixel_width; + } + + //! Maximum length to trace + CELER_FUNCTION real_type max_length() const { return scalars_.max_length; } + + //! Number of pixels along the direction of travel + CELER_FUNCTION size_type max_index() const { return scalars_.dims[1]; } + + // Set pixel value + inline CELER_FUNCTION void set_pixel(size_type col, int value); + + private: + ImageParamsScalars const& scalars_; + StateRef const& state_; + size_type row_index_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with image data and thread ID. + */ +CELER_FUNCTION +ImageLineView::ImageLineView(ParamsRef const& params, + StateRef const& state, + size_type row_index) + : scalars_{params.scalars}, state_{state}, row_index_{row_index} +{ + CELER_EXPECT(row_index_ < scalars_.dims[0]); +} + +//---------------------------------------------------------------------------// +/*! + * Calculate starting position. + */ +CELER_FUNCTION auto ImageLineView::start_pos() const -> Real3 +{ + real_type down_offset = (row_index_ + real_type(0.5)) + * scalars_.pixel_width; + Real3 result = scalars_.origin; + axpy(down_offset, scalars_.down, &result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Set the value for a pixel. + */ +CELER_FUNCTION void ImageLineView::set_pixel(size_type col, int value) +{ + CELER_EXPECT(col < scalars_.dims[1]); + size_type idx = row_index_ * scalars_.dims[1] + col; + + CELER_ASSERT(idx < state_.image.size()); + state_.image[ItemId{idx}] = value; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/Raytracer.hh b/src/geocel/rasterize/Raytracer.hh new file mode 100644 index 0000000000..f4bbe42fa4 --- /dev/null +++ b/src/geocel/rasterize/Raytracer.hh @@ -0,0 +1,182 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/Raytracer.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/math/Algorithms.hh" + +#include "ImageLineView.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Trace each pixel along a line. + * + * \tparam GTV Geometry Track View + * \tparam F Calculate the pixel value (result_type) given GTV + */ +template +class Raytracer +{ + public: + //!@{ + //! \name Type aliases + using result_type = int; + //!@} + + public: + // Construct from geo track view, function to calculate ID, and image + inline CELER_FUNCTION + Raytracer(GTV&& geo, F&& calc_id, ImageLineView const& image); + + // Calculate the value for the next pixel + inline CELER_FUNCTION result_type operator()(size_type pix); + + private: + //// DATA //// + + GTV geo_; + F calc_id_; + ImageLineView const& image_; + + size_type pixel_; + + //// HELPER FUNCTIONS //// + + // Initialize the geometry at this pixel index + inline CELER_FUNCTION void initialize_at_pixel(size_type pix); + + //! Sentinel value for needing the pixel to be reset + static CELER_CONSTEXPR_FUNCTION size_type invalid_pixel() + { + return static_cast(-1); + } + + //! Stop tracking the pixel if this many crossings occur + static CELER_CONSTEXPR_FUNCTION short int max_crossings_per_pixel() + { + return 32; + } +}; + +//---------------------------------------------------------------------------// +// DEDUCTION GUIDES +//---------------------------------------------------------------------------// + +template +CELER_FUNCTION Raytracer(GTV&&, F&&, ImageLineView const&) + -> Raytracer; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct from geo track view, function to calculate ID, and image. + */ +template +CELER_FUNCTION +Raytracer::Raytracer(GTV&& geo, F&& calc_id, ImageLineView const& image) + : geo_{celeritas::forward(geo)} + , calc_id_{celeritas::forward(calc_id)} + , image_{image} + , pixel_{invalid_pixel()} +{ +} + +//---------------------------------------------------------------------------// +/*! + * Calculate the value for a single pixel. + */ +template +CELER_FUNCTION auto Raytracer::operator()(size_type pix) -> result_type +{ + if (pix != pixel_) + { + this->initialize_at_pixel(pix); + } + + real_type pix_dist = image_.pixel_width(); + real_type max_dist = 0; + int cur_id = this->calc_id_(geo_); + int max_id = cur_id; + auto abort_counter = this->max_crossings_per_pixel(); + + auto next = geo_.find_next_step(pix_dist); + while (next.boundary && pix_dist > 0) + { + CELER_ASSERT(next.distance <= pix_dist); + // Move to geometry boundary + pix_dist -= next.distance; + + if (max_id == cur_id) + { + max_dist += next.distance; + } + else if (next.distance > max_dist) + { + max_dist = next.distance; + max_id = cur_id; + } + + // Cross surface and update post-crossing ID + geo_.move_to_boundary(); + geo_.cross_boundary(); + cur_id = this->calc_id_(geo_); + + if (--abort_counter == 0) + { + // Give up and move to end of pixel, reinitialize on the next call + pix_dist = 0; + } + if (pix_dist > 0) + { + // Next movement is to end of geo_ or pixel + next = geo_.find_next_step(pix_dist); + } + } + + if (pix_dist > 0) + { + // Move to pixel boundary + geo_.move_internal(pix_dist); + if (pix_dist > max_dist) + { + max_dist = pix_dist; + max_id = cur_id; + } + } + + if (abort_counter != 0) + { + ++pixel_; + } + else + { + pixel_ = invalid_pixel(); + } + + return max_id; +} + +//---------------------------------------------------------------------------// +/*! + * Initialize the geometry at the given pixel position. + */ +template +CELER_FUNCTION void Raytracer::initialize_at_pixel(size_type pix) +{ + CELER_EXPECT(pix < image_.max_index()); + + GeoTrackInitializer init{image_.start_pos(), image_.start_dir()}; + axpy(pix * image_.pixel_width(), init.dir, &init.pos); + geo_ = init; + pixel_ = pix; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/test/geocel/CMakeLists.txt b/test/geocel/CMakeLists.txt index 708daa0a70..5b0817e14d 100644 --- a/test/geocel/CMakeLists.txt +++ b/test/geocel/CMakeLists.txt @@ -103,6 +103,9 @@ if(CELERITAS_USE_VecGeom) ) endif() +#-----------------------------------------------------------------------------# +# Geant4 + if(CELERITAS_USE_Geant4) celeritas_add_test(ScopedGeantLogger.test.cc LINK_LIBRARIES ${_g4_global_libs}) endif() @@ -113,3 +116,11 @@ if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") endif() #-----------------------------------------------------------------------------# +# Rasterization + +set(CELERITASTEST_PREFIX geocel/rasterize) + +celeritas_add_test(rasterize/Image.test.cc) +celeritas_add_test(rasterize/Raytracer.test.cc) + +#-----------------------------------------------------------------------------# diff --git a/test/geocel/MockGeoTrackView.hh b/test/geocel/MockGeoTrackView.hh new file mode 100644 index 0000000000..dabd0379aa --- /dev/null +++ b/test/geocel/MockGeoTrackView.hh @@ -0,0 +1,208 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/MockGeoTrackView.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Assert.hh" +#include "corecel/math/ArrayUtils.hh" +#include "corecel/math/SoftEqual.hh" +#include "geocel/Types.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +/*! + * Mock track view for testing. + * + * The mock track view has boundaries at every integer z, with a volume ID + * that's equal to the floor of the current position. The geometry is outside + * for z < 0. + * + * \note This class is only used by the raytracer test at present, so it + * doesn't have full functionality. Consider using ORANGE geometry for more + * complex test cases. + */ +class MockGeoTrackView +{ + public: + //!@{ + //! \name Type aliases + using Initializer_t = GeoTrackInitializer; + //!@} + + public: + //! Default has uninitialized state + MockGeoTrackView() = default; + + // Initialize the state + inline CELER_FUNCTION MockGeoTrackView& + operator=(Initializer_t const& init); + + //!@{ + //! State accessors + CELER_FORCEINLINE_FUNCTION Real3 const& pos() const { return state_.pos; } + CELER_FORCEINLINE_FUNCTION Real3 const& dir() const { return state_.dir; } + //!@} + + // Get the volume ID in the current cell. + CELER_FORCEINLINE_FUNCTION VolumeId volume_id() const; + + // Whether the track is outside the valid geometry region + CELER_FORCEINLINE_FUNCTION bool is_outside() const; + // Whether the track is exactly on a surface + CELER_FORCEINLINE_FUNCTION bool is_on_boundary() const; + + // Find the distance to the next boundary, up to and including a step + inline CELER_FUNCTION Propagation find_next_step(real_type max_step); + + // Move to the boundary in preparation for crossing it + inline CELER_FUNCTION void move_to_boundary(); + + // Move within the volume + inline CELER_FUNCTION void move_internal(real_type step); + + // Cross from one side of the current surface to the other + inline CELER_FUNCTION void cross_boundary(); + + //! Number of times initialized with a GeoTrackInitializer + int init_count() const { return init_count_; } + + private: + GeoTrackInitializer state_; + real_type next_step_{}; + int init_count_{0}; + + // Z value + CELER_FORCEINLINE_FUNCTION real_type z() const { return state_.pos[2]; } +}; + +//---------------------------------------------------------------------------// +/*! + * Construct the state. + */ +CELER_FUNCTION MockGeoTrackView& +MockGeoTrackView::operator=(Initializer_t const& init) +{ + ++init_count_; + this->state_ = init; + return *this; +} + +//---------------------------------------------------------------------------// +/*! + * Get the volume ID in the current cell. + * + * XXX this doesn't correctly handle the logical change in state for boundary + * crossing. + */ +CELER_FUNCTION VolumeId MockGeoTrackView::volume_id() const +{ + CELER_EXPECT(!this->is_outside()); + return VolumeId{static_cast(std::floor(this->z()))}; +} + +//---------------------------------------------------------------------------// +/*! + * Whether the track is outside the valid geometry region. + */ +CELER_FUNCTION bool MockGeoTrackView::is_outside() const +{ + return this->z() <= 0; +} + +//---------------------------------------------------------------------------// +/*! + * Whether the track is on the boundary of a volume. + */ +CELER_FUNCTION bool MockGeoTrackView::is_on_boundary() const +{ + return soft_mod(this->z(), real_type{1}); +} + +//---------------------------------------------------------------------------// +/*! + * Find the distance to the next geometric boundary. + */ +CELER_FUNCTION Propagation MockGeoTrackView::find_next_step(real_type max_step) +{ + CELER_EXPECT(!this->is_outside()); + CELER_EXPECT(max_step > 0); + + real_type z = state_.pos[2]; + real_type w = state_.dir[2]; + real_type next_surf{100000}; + if (w > 0) + { + next_surf = (1 - std::fmod(z, real_type{1})) / w; + } + else if (w < 0) + { + next_surf = std::fmod(z, real_type{1}) / -w; + } + next_step_ = std::fmin(next_surf, max_step); + + Propagation result; + result.boundary = next_step_ < max_step; + result.distance = next_step_; + + CELER_ENSURE(result.distance > 0); + CELER_ENSURE(result.distance <= max_step); + CELER_ENSURE(result.boundary || result.distance == max_step); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Move to the next boundary but don't cross yet. + */ +CELER_FUNCTION void MockGeoTrackView::move_to_boundary() +{ + // Move next step + this->move_internal(next_step_); + next_step_ = 0; + + CELER_ENSURE(this->is_on_boundary()); +} + +//---------------------------------------------------------------------------// +/*! + * Cross from one side of the current surface to the other. + * + * The position *must* be on the boundary following a move-to-boundary. + */ +CELER_FUNCTION void MockGeoTrackView::cross_boundary() +{ + CELER_EXPECT(this->is_on_boundary()); + + // Make sure we're exactly on the value + this->state_.pos[2] = std::round(this->state_.pos[2]); + + CELER_ENSURE(this->is_on_boundary()); +} + +//---------------------------------------------------------------------------// +/*! + * Move within the current volume. + * + * The straight-line distance *must* be less than the distance to the + * boundary. + */ +CELER_FUNCTION void MockGeoTrackView::move_internal(real_type dist) +{ + CELER_EXPECT(dist > 0 && dist <= next_step_); + + axpy(dist, state_.dir, &state_.pos); + next_step_ -= dist; +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/geocel/rasterize/Image.test.cc b/test/geocel/rasterize/Image.test.cc new file mode 100644 index 0000000000..3f1a0dac74 --- /dev/null +++ b/test/geocel/rasterize/Image.test.cc @@ -0,0 +1,117 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/Image.test.cc +//---------------------------------------------------------------------------// +#include "geocel/rasterize/Image.hh" + +#include "geocel/rasterize/ImageLineView.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class ImageTest : public ::celeritas::test::Test +{ + protected: + using Size2 = ImageParamsScalars::Size2; + + void SetUp() override {} +}; + +TEST_F(ImageTest, errors) +{ + ImageInput inp; + // Missing pixel count + inp.upper_right = {1, 1, 0}; + EXPECT_THROW(ImageParams{inp}, RuntimeError); + + // Zero image size + inp.vertical_pixels = 128; + inp.upper_right = inp.lower_left; + EXPECT_THROW(ImageParams{inp}, RuntimeError); + + // Zero horizontal divisor + inp.upper_right = {1, 1, 0}; + inp.horizontal_divisor = 0; + EXPECT_THROW(ImageParams{inp}, RuntimeError); + + // Invalid basis function (orthogonal) + inp.upper_right = {1, 1, 0}; + inp.horizontal_divisor = 1; + inp.rightward = {0, 0, 1}; + EXPECT_THROW(ImageParams{inp}, RuntimeError); + + // Invalid basis function (backward) + inp.rightward = {-1, 0, 0}; + EXPECT_THROW(ImageParams{inp}, RuntimeError); +} + +//! Pixel counts divide exactly into image dimensions +TEST_F(ImageTest, exact) +{ + ImageInput inp; + inp.upper_right = {128, 32, 0}; + inp.vertical_pixels = 16; + inp.horizontal_divisor = 16; + + auto params = std::make_shared(inp); + auto const& scalars = params->scalars(); + EXPECT_VEC_SOFT_EQ((Real3{0, 32, 0}), scalars.origin); + EXPECT_VEC_SOFT_EQ((Real3{0, -1, 0}), scalars.down); + EXPECT_SOFT_EQ(2, scalars.pixel_width); + EXPECT_VEC_EQ((Size2{16, 16 * 4}), scalars.dims); + EXPECT_SOFT_EQ(128, scalars.max_length); + EXPECT_EQ(16, params->num_lines()); + EXPECT_EQ(16 * 16 * 4, params->num_pixels()); + + Image img(params); + { + ImageLineView line{params->host_ref(), img.ref(), 0}; + EXPECT_EQ(64, line.max_index()); + line.set_pixel(0, 123); + line.set_pixel(2, 345); + EXPECT_VEC_SOFT_EQ((Real3{0, 31, 0}), line.start_pos()); + } + { + ImageLineView line{params->host_ref(), img.ref(), 1}; + line.set_pixel(1, 567); + EXPECT_VEC_SOFT_EQ((Real3{0, 29, 0}), line.start_pos()); + } + + std::vector result(params->num_pixels()); + img.copy_to_host(make_span(result)); + EXPECT_EQ(123, result[0]); + EXPECT_EQ(-1, result[1]); + EXPECT_EQ(345, result[2]); + EXPECT_EQ(567, result[65]); +} + +//! Horizontal pixel count is padded beyond the requested window +TEST_F(ImageTest, inexact) +{ + ImageInput inp; + inp.lower_left = {-1, 2, -1}; + inp.upper_right = {6, 2, 4}; + inp.vertical_pixels = 256; + inp.rightward = {0, 0, 1}; // +z + inp.horizontal_divisor = 32; + + auto params = std::make_shared(inp); + auto const& scalars = params->scalars(); + EXPECT_VEC_SOFT_EQ((Real3{6, 2, -1}), scalars.origin); + EXPECT_VEC_SOFT_EQ((Real3{-1, 0, 0}), scalars.down); + EXPECT_SOFT_EQ(real_type{7} / 256, scalars.pixel_width); + EXPECT_VEC_EQ((Size2{256, 192}), scalars.dims); + EXPECT_SOFT_EQ(real_type{5}, scalars.max_length); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/geocel/rasterize/Raytracer.test.cc b/test/geocel/rasterize/Raytracer.test.cc new file mode 100644 index 0000000000..e8dae986b2 --- /dev/null +++ b/test/geocel/rasterize/Raytracer.test.cc @@ -0,0 +1,143 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/Raytracer.test.cc +//---------------------------------------------------------------------------// +#include "geocel/rasterize/Raytracer.hh" + +#include + +#include "geocel/rasterize/Image.hh" + +#include "celeritas_test.hh" +#include "../MockGeoTrackView.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +int calc_id(MockGeoTrackView const& geo) +{ + if (geo.is_outside()) + return -1; + return static_cast(geo.pos()[2]); +} + +//---------------------------------------------------------------------------// +TEST(MockGeo, tracking) +{ + MockGeoTrackView geo; + geo = GeoTrackInitializer{{0, 0, 5.25}, {0, 0, 1}}; + auto next_step = geo.find_next_step(2.0); + EXPECT_TRUE(next_step.boundary); + EXPECT_SOFT_EQ(0.75, next_step.distance); + geo.move_to_boundary(); + EXPECT_REAL_EQ(6.0, geo.pos()[2]); + next_step = geo.find_next_step(1.5); + EXPECT_TRUE(next_step.boundary); + EXPECT_SOFT_EQ(1.0, next_step.distance); + geo.move_to_boundary(); + next_step = geo.find_next_step(0.5); + EXPECT_FALSE(next_step.boundary); + EXPECT_SOFT_EQ(0.5, next_step.distance); + geo.move_internal(0.25); + next_step = geo.find_next_step(1.0); + EXPECT_TRUE(next_step.boundary); + EXPECT_SOFT_EQ(0.75, next_step.distance); + + geo = GeoTrackInitializer{{0, 0, 4.75}, make_unit_vector(Real3{0, 0, -1})}; + next_step = geo.find_next_step(100.0); + EXPECT_TRUE(next_step.boundary); + EXPECT_SOFT_EQ(0.75, next_step.distance); + + geo = GeoTrackInitializer{{0, 0, 9.75}, make_unit_vector(Real3{0, 4, -3})}; + next_step = geo.find_next_step(100.0); + EXPECT_TRUE(next_step.boundary); + EXPECT_SOFT_EQ(0.75 * 5.0 / 3.0, next_step.distance); +} + +//---------------------------------------------------------------------------// + +class RaytracerTest : public ::celeritas::test::Test +{ + protected: + void SetUp() override {} +}; + +TEST_F(RaytracerTest, exact) +{ + ImageInput inp; + inp.rightward = {0, 0, 1}; + inp.lower_left = {0, 0, 32}; + inp.upper_right = {0, 16, 160}; + inp.vertical_pixels = 16; + inp.horizontal_divisor = 1; + + // Start at {0, 10.5, 32} and move along +z + auto params = std::make_shared(inp); + Image img(params); + ImageLineView line{params->host_ref(), img.ref(), 5}; + + MockGeoTrackView geo; + EXPECT_EQ(0, geo.init_count()); + + Raytracer trace{geo, calc_id, line}; + EXPECT_EQ(32, trace(0)); + EXPECT_EQ(1, geo.init_count()); + EXPECT_VEC_SOFT_EQ((Real3{0, 10.5, 33}), geo.pos()); + EXPECT_EQ(33, trace(1)); + EXPECT_VEC_SOFT_EQ((Real3{0, 10.5, 34}), geo.pos()); + EXPECT_EQ(1, geo.init_count()); + EXPECT_EQ(40, trace(8)); + EXPECT_EQ(2, geo.init_count()); +} + +TEST_F(RaytracerTest, offset) +{ + ImageInput inp; + inp.rightward = {0, 0, 1}; + inp.lower_left = {0, 0, 1.75}; + inp.upper_right = {0, 1.0, 16.75}; + inp.vertical_pixels = 1; + inp.horizontal_divisor = 1; + + auto params = std::make_shared(inp); + Image img(params); + ImageLineView line{params->host_ref(), img.ref(), 0}; + + MockGeoTrackView geo; + Raytracer trace{geo, calc_id, line}; + for (auto col : range(10)) + { + EXPECT_EQ(col + 2, trace(col)); + } + EXPECT_EQ(1, geo.init_count()); +} + +TEST_F(RaytracerTest, megapixels) +{ + ImageInput inp; + inp.rightward = {0, 0, 1}; + inp.lower_left = {0, 0, 1}; + inp.upper_right = {0, 64, 64 * 8}; + inp.vertical_pixels = 1; + inp.horizontal_divisor = 1; + + auto params = std::make_shared(inp); + Image img(params); + ImageLineView line{params->host_ref(), img.ref(), 0}; + + MockGeoTrackView geo; + Raytracer trace{geo, calc_id, line}; + EXPECT_EQ(1, trace(0)); + EXPECT_EQ(1, geo.init_count()); + EXPECT_EQ(65, trace(1)); + EXPECT_EQ(2, geo.init_count()); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas From c1f3f81b7c6b261422583a5f22c39900f29f91fe Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 24 Apr 2024 13:29:47 -0400 Subject: [PATCH 16/59] Add test for assertion messages (#1198) * Fix assertion message formatting * Fix and improve assertion output with test * Avoid using gmock to fix CI --- src/corecel/Assert.cc | 15 ++- src/corecel/Assert.hh | 2 +- src/corecel/io/Join.hh | 7 +- src/corecel/io/detail/ReprImpl.hh | 13 +++ test/corecel/Assert.test.cc | 161 ++++++++++++++++++++++++++++++ test/corecel/CMakeLists.txt | 3 + 6 files changed, 193 insertions(+), 8 deletions(-) create mode 100644 test/corecel/Assert.test.cc diff --git a/src/corecel/Assert.cc b/src/corecel/Assert.cc index 92651c3cb7..981d3900f1 100644 --- a/src/corecel/Assert.cc +++ b/src/corecel/Assert.cc @@ -68,7 +68,7 @@ std::string build_runtime_error_msg(RuntimeErrorDetails const& d) { msg << "required dependency is disabled in this build: "; } - else if (d.which == "not implemented") + else if (d.which == "implementation") { msg << "feature is not yet implemented: "; } @@ -77,14 +77,21 @@ std::string build_runtime_error_msg(RuntimeErrorDetails const& d) if (verbose_message || d.what.empty() || d.which != "runtime") { msg << '\n' - << color_code('W') << (d.file.empty() ? "unknown source" : d.file); - if (d.line) + << color_code(d.condition.empty() ? 'x' : 'W') + << (d.file.empty() ? "unknown source" : d.file); + if (d.line && !d.file.empty()) { msg << ':' << d.line; } + + msg << ':' << color_code(' '); if (!d.condition.empty()) { - msg << ':' << color_code(' ') << " '" << d.condition << "' failed"; + msg << " '" << d.condition << '\'' << " failed"; + } + else + { + msg << " failure"; } } diff --git a/src/corecel/Assert.hh b/src/corecel/Assert.hh index 5deed98dfd..3d8ae9886a 100644 --- a/src/corecel/Assert.hh +++ b/src/corecel/Assert.hh @@ -239,7 +239,7 @@ #define CELER_NOT_CONFIGURED(WHAT) \ CELER_RUNTIME_THROW("configuration", WHAT, {}) #define CELER_NOT_IMPLEMENTED(WHAT) \ - CELER_RUNTIME_THROW("not implemented", WHAT, {}) + CELER_RUNTIME_THROW("implementation", WHAT, {}) /*! * \def CELER_CUDA_CALL diff --git a/src/corecel/io/Join.hh b/src/corecel/io/Join.hh index 035c2e9b3d..df1c64f263 100644 --- a/src/corecel/io/Join.hh +++ b/src/corecel/io/Join.hh @@ -22,9 +22,10 @@ namespace celeritas cout << celeritas::join(foo.begin(), foo.end(), ", ") << endl \endcode * - * The result is a thin class that is implicitly convertible to a std::string - * and is streamable. (It can explicitly be converted to a string with the - * \c .str() method). By doing this instead of returning a std::string, large + * The result is a thin class that is streamable. (It can explicitly be + * converted to a string with the + * \c to_string method). By doing this instead of returning a std::string, + large * and dynamic containers can be e.g. saved to disk. */ template diff --git a/src/corecel/io/detail/ReprImpl.hh b/src/corecel/io/detail/ReprImpl.hh index 919d07c9e2..4ee521d239 100644 --- a/src/corecel/io/detail/ReprImpl.hh +++ b/src/corecel/io/detail/ReprImpl.hh @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -58,6 +59,18 @@ std::ostream& operator<<(std::ostream& os, Repr const& s) return os; } +//---------------------------------------------------------------------------// +/*! + * Convert a streamable object to a string. + */ +template +std::string to_string(Repr const& s) +{ + std::ostringstream os; + os << s; + return os.str(); +} + //---------------------------------------------------------------------------// // HELPER FUNCTION DECLARATIONS //---------------------------------------------------------------------------// diff --git a/test/corecel/Assert.test.cc b/test/corecel/Assert.test.cc new file mode 100644 index 0000000000..70c6ca6af6 --- /dev/null +++ b/test/corecel/Assert.test.cc @@ -0,0 +1,161 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file corecel/Assert.test.cc +//---------------------------------------------------------------------------// +#include "corecel/Assert.hh" + +#include +#include + +#include "corecel/io/Repr.hh" +#include "corecel/sys/Environment.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class AssertTest : public ::celeritas::test::Test +{ + protected: + static void SetUpTestSuite() + { + EXPECT_FALSE(celeritas::getenv("CELER_COLOR").empty() + && celeritas::getenv("GTEST_COLOR").empty()) + << "Color must be enabled for this test"; + EXPECT_FALSE(celeritas::getenv("CELER_LOG").empty()) + << "Logging (for verbose output) must be enabled for this test"; + } +}; + +TEST_F(AssertTest, debug_error) +{ + DebugErrorDetails details; + details.which = DebugErrorType::internal; + details.condition = "2 + 2 == 5"; + details.file = "Assert.test.cc"; + details.line = 123; + + EXPECT_STREQ( + "\x1B[37;1mAssert.test.cc:123:\x1B[0m\nceleritas: \x1B[31;1minternal " + "assertion failed: \x1B[37;2m2 + 2 == 5\x1B[0m", + DebugError{std::move(details)}.what()); +} + +TEST_F(AssertTest, runtime_error) +{ + try + { + CELER_NOT_CONFIGURED("foo"); + } + catch (RuntimeError const& e) + { + EXPECT_TRUE(std::string{e.what()}.find("configuration error:") + != std::string::npos); + EXPECT_TRUE(std::string{e.what()}.find("required dependency is " + "disabled in this build: foo") + != std::string::npos) + << e.what(); + } + try + { + CELER_NOT_IMPLEMENTED("bar"); + } + catch (RuntimeError const& e) + { + EXPECT_TRUE(std::string{e.what()}.find("implementation error:") + != std::string::npos); + EXPECT_TRUE(std::string{e.what()}.find("feature is not yet " + "implemented: bar") + != std::string::npos) + << e.what(); + } + try + { + CELER_VALIDATE(false, << "this is not OK"); + } + catch (RuntimeError const& e) + { + EXPECT_TRUE(std::string{e.what()}.find("runtime error: \x1B[0mthis is " + "not OK") + != std::string::npos) + << e.what(); + } +} + +TEST_F(AssertTest, runtime_error_variations) +{ + std::vector messages; + // Loop over all combinations of missing data + for (auto bitmask : range(1 << 4)) + { + RuntimeErrorDetails details; + if (bitmask & 0x1) + { + details.which = "runtime"; + } + if (bitmask & 0x2) + { + details.what = "bad things happened"; + } + if (bitmask & 0x4) + { + details.condition = "2 + 2 == 5"; + } + if (bitmask & 0x8) + { + details.file = "Assert.test.cc"; + if (bitmask & 0x1) + { + details.line = 123; + } + } + // Generate the message + RuntimeError err{std::move(details)}; + messages.push_back(err.what()); + } + static std::string const expected_messages[] = { + "celeritas: \x1b[31;1munknown error: \x1b[0m\n\x1b[37;2munknown " + "source:\x1b[0m failure", + "celeritas: \x1b[31;1mruntime error: \x1b[0m\n\x1b[37;2munknown " + "source:\x1b[0m failure", + "celeritas: \x1b[31;1munknown error: \x1b[0mbad things " + "happened\n\x1b[37;2munknown source:\x1b[0m failure", + "celeritas: \x1b[31;1mruntime error: \x1b[0mbad things " + "happened\n\x1b[37;2munknown source:\x1b[0m failure", + "celeritas: \x1b[31;1munknown error: \x1b[0m\n\x1b[37;1munknown " + "source:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1mruntime error: \x1b[0m\n\x1b[37;1munknown " + "source:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1munknown error: \x1b[0mbad things " + "happened\n\x1b[37;1munknown source:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1mruntime error: \x1b[0mbad things " + "happened\n\x1b[37;1munknown source:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1munknown error: " + "\x1b[0m\n\x1b[37;2mAssert.test.cc:\x1b[0m failure", + "celeritas: \x1b[31;1mruntime error: " + "\x1b[0m\n\x1b[37;2mAssert.test.cc:123:\x1b[0m failure", + "celeritas: \x1b[31;1munknown error: \x1b[0mbad things " + "happened\n\x1b[37;2mAssert.test.cc:\x1b[0m failure", + "celeritas: \x1b[31;1mruntime error: \x1b[0mbad things " + "happened\n\x1b[37;2mAssert.test.cc:123:\x1b[0m failure", + "celeritas: \x1b[31;1munknown error: " + "\x1b[0m\n\x1b[37;1mAssert.test.cc:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1mruntime error: " + "\x1b[0m\n\x1b[37;1mAssert.test.cc:123:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1munknown error: \x1b[0mbad things " + "happened\n\x1b[37;1mAssert.test.cc:\x1b[0m '2 + 2 == 5' failed", + "celeritas: \x1b[31;1mruntime error: \x1b[0mbad things " + "happened\n\x1b[37;1mAssert.test.cc:123:\x1b[0m '2 + 2 == 5' failed"}; + EXPECT_VEC_EQ(expected_messages, messages); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/corecel/CMakeLists.txt b/test/corecel/CMakeLists.txt index 599f03d572..d9fec0401e 100644 --- a/test/corecel/CMakeLists.txt +++ b/test/corecel/CMakeLists.txt @@ -18,6 +18,9 @@ celeritas_setup_tests(SERIAL PREFIX corecel #-----------------------------------------------------------------------------# # TESTS #-----------------------------------------------------------------------------# +celeritas_add_test(Assert.test.cc + ENVIRONMENT "CELER_COLOR=1;CELER_LOG=debug" +) celeritas_add_test(OpaqueId.test.cc) # Cont From 13fdf1c6372ba265c884fd695c8608a0ae5d3731 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Wed, 24 Apr 2024 13:00:38 -0500 Subject: [PATCH 17/59] Fix build error from Geant4@11.1.0 (#1199) --- src/accel/FastSimulationOffload.cc | 4 ++-- src/accel/FastSimulationOffload.hh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/accel/FastSimulationOffload.cc b/src/accel/FastSimulationOffload.cc index 0acbfe1caa..dd7f2d260f 100644 --- a/src/accel/FastSimulationOffload.cc +++ b/src/accel/FastSimulationOffload.cc @@ -24,7 +24,7 @@ FastSimulationOffload::FastSimulationOffload(G4String const& name, LocalTransporter* local) : G4VFastSimulationModel(name), params_(params), transport_(local) { - CELER_VALIDATE(G4VERSION_NUMBER > 1110, + CELER_VALIDATE(G4VERSION_NUMBER >= 1110, << "the current version of Geant4 (" << G4VERSION_NUMBER << ") is too old to support the fast simulation Flush() " "interface"); @@ -45,7 +45,7 @@ FastSimulationOffload::FastSimulationOffload(G4String const& name, LocalTransporter* local) : G4VFastSimulationModel(name, region), params_(params), transport_(local) { - CELER_VALIDATE(G4VERSION_NUMBER > 1110, + CELER_VALIDATE(G4VERSION_NUMBER >= 1110, << "the current version of Geant4 (" << G4VERSION_NUMBER << ") is too old to support the fast simulation Flush() " "interface"); diff --git a/src/accel/FastSimulationOffload.hh b/src/accel/FastSimulationOffload.hh index 61b8d57da1..89eafd17f6 100644 --- a/src/accel/FastSimulationOffload.hh +++ b/src/accel/FastSimulationOffload.hh @@ -48,7 +48,7 @@ class FastSimulationOffload final : public G4VFastSimulationModel // Apply model void DoIt(G4FastTrack const& track, G4FastStep& step) override; -#if G4VERSION_NUMBER > 1110 +#if G4VERSION_NUMBER >= 1110 //! Complete processing of buffered tracks void Flush() override; #endif From 89336e59b66fad695833cba4c65fac976918e60e Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 24 Apr 2024 16:03:07 -0400 Subject: [PATCH 18/59] Add Windows/Linux no-dependency builds (#1196) * Increase jenkin timeouts thanks to kokkos * Refine sphinx version requirement and update doc version * Add ultralite workflow * Remove discontinued systems * Add ultralite windows workflow * Allow ultralite builds to fail --- .github/workflows/build-fast.yml | 2 +- .github/workflows/build-spack.yml | 2 +- .github/workflows/build-ultralite.yml | 111 ++++++++++++++++++ .github/workflows/doc.yml | 2 +- .github/workflows/pr.yml | 4 +- .github/workflows/push.yml | 2 + .jenkins | 2 +- scripts/cmake-presets/ci-ubuntu-github.json | 11 ++ scripts/cmake-presets/ci-windows-github.json | 72 ++++++++++++ scripts/cmake-presets/crusher.json | 97 --------------- scripts/cmake-presets/summit.json | 102 ---------------- ...{requirements.txt => doc-requirements.txt} | 2 +- scripts/env/crusher.sh | 40 ------- scripts/env/summit.sh | 14 --- 14 files changed, 204 insertions(+), 259 deletions(-) create mode 100644 .github/workflows/build-ultralite.yml create mode 100644 scripts/cmake-presets/ci-windows-github.json delete mode 100644 scripts/cmake-presets/crusher.json delete mode 100644 scripts/cmake-presets/summit.json rename scripts/{requirements.txt => doc-requirements.txt} (71%) delete mode 100755 scripts/env/crusher.sh delete mode 100755 scripts/env/summit.sh diff --git a/.github/workflows/build-fast.yml b/.github/workflows/build-fast.yml index 121e4a80a4..203b60e66e 100644 --- a/.github/workflows/build-fast.yml +++ b/.github/workflows/build-fast.yml @@ -76,7 +76,7 @@ jobs: - name: Run tests working-directory: build run: | - ctest --parallel 2 --timeout 15 --output-on-failure \ + ctest --parallel $(nproc) --timeout 15 --output-on-failure \ --test-output-size-passed=32768 --test-output-size-failed=1048576 - name: Install working-directory: build diff --git a/.github/workflows/build-spack.yml b/.github/workflows/build-spack.yml index 08b3090f31..7c53acd4b4 100644 --- a/.github/workflows/build-spack.yml +++ b/.github/workflows/build-spack.yml @@ -8,7 +8,7 @@ concurrency: group: build-spack-${{github.ref}}-${{github.event.pull_request.number || github.run_number}}-${{github.workflow}} env: - SPACK_REF: 968ad02473b12f6305cc1fe19f2a0d706f171154 + SPACK_REF: 968ad02473b12f6305cc1fe19f2a0d706f171154 # develop-2024-03-10-70-g968ad02473 jobs: spack: diff --git a/.github/workflows/build-ultralite.yml b/.github/workflows/build-ultralite.yml new file mode 100644 index 0000000000..f1bb730a1c --- /dev/null +++ b/.github/workflows/build-ultralite.yml @@ -0,0 +1,111 @@ +# Build directly on the GitHub runner with caching +name: build-ultralite +on: + workflow_dispatch: + workflow_call: + +concurrency: + group: build-ultralite-${{github.ref}}-${{github.event.pull_request.number || github.run_number}}-${{github.workflow}} + cancel-in-progress: true + +jobs: + linux: + continue-on-error: true + name: ultralite-ubuntu + env: + CCACHE_DIR: "${{github.workspace}}/.ccache" + CCACHE_MAXSIZE: "20Mi" + CMAKE_PRESET: ultralite + runs-on: ubuntu-latest + steps: + - name: Install dependencies + run: | + sudo apt-get -q -y update + sudo apt-get -q -y install \ + ccache cmake ninja-build + - name: Check out Celeritas + uses: actions/checkout@v4 + - name: Cache ccache + uses: actions/cache@v4 + with: + path: ${{env.CCACHE_DIR}} + key: ccache-ultralite-${{github.run_id}} + restore-keys: | + ccache-ultralite + - name: Zero ccache stats + run: | + ccache -z + - name: Configure Celeritas + run: | + ln -fs scripts/cmake-presets/ci-ubuntu-github.json CMakeUserPresets.json + cmake --preset=${CMAKE_PRESET} \ + -DCeleritas_GIT_DESCRIBE="${{github.event.pull_request + && format(';-pr.{0};', github.event.pull_request.number) + || format(';-{0};', github.ref_name)}}" + - name: Build all + working-directory: build + run: | + ninja + - name: Run tests + working-directory: build + run: | + ctest --parallel $(nproc) --timeout 15 --output-on-failure + - name: Install + working-directory: build + run: | + ninja install + - name: Check installation + working-directory: install + run: | + ./bin/celer-sim --version + - name: Show ccache stats + if: ${{!cancelled()}} + run: | + ccache -s + windows: + continue-on-error: true + name: ultralite-windows + env: + CCACHE_DIR: "${{github.workspace}}\\.ccache" + CCACHE_MAXSIZE: "20Mi" + CMAKE_PRESET: ultralite + runs-on: windows-latest + steps: + - name: Install dependencies + run: | + choco install ninja ccache + - name: Check out Celeritas + uses: actions/checkout@v4 + - name: Set up MSVC + uses: ilammy/msvc-dev-cmd@v1 + - name: Cache ccache + uses: actions/cache@v4 + with: + path: ${{env.CCACHE_DIR}} + key: ccache-ultralite-${{github.run_id}} + restore-keys: | + ccache-ultralite + - name: Zero ccache stats + run: | + ccache -z + - name: Configure Celeritas + shell: pwsh + run: | + Copy-Item scripts/cmake-presets/ci-windows-github.json -Destination CMakeUserPresets.json + cmake --preset=$Env:CMAKE_PRESET ` + -DCeleritas_GIT_DESCRIBE="${{github.event.pull_request + && format(';-pr.{0};', github.event.pull_request.number) + || format(';-{0};', github.ref_name)}}" + - name: Build all + run: | + cmake --build --preset=$Env:CMAKE_PRESET + - name: Test all + continue-on-error: true + run: | + ctest --preset=$Env:CMAKE_PRESET + - name: Show ccache stats + if: ${{!cancelled()}} + run: | + ccache -s + +# vim: set nowrap tw=100: diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 156e6f6877..7e84c165de 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -74,7 +74,7 @@ jobs: - name: Install Python packages run: | pip install --upgrade pip - pip install -r scripts/requirements.txt + pip install -r scripts/doc-requirements.txt - name: Configure celeritas run: | mkdir build && cd build diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index eb7abbabd0..4c55d04d3a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -21,10 +21,12 @@ concurrency: jobs: build-fast: uses: ./.github/workflows/build-fast.yml + build-ultralite: + uses: ./.github/workflows/build-ultralite.yml doc: uses: ./.github/workflows/doc.yml all-prechecks: - needs: [build-fast, doc] + needs: [build-fast, build-ultralite, doc] runs-on: ubuntu-latest steps: - name: Success diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 96357f7ba4..1cabaf57ae 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -16,6 +16,8 @@ concurrency: cancel-in-progress: true jobs: + build-ultralite: + uses: ./.github/workflows/build-ultralite.yml build-fast: uses: ./.github/workflows/build-fast.yml build-docker: diff --git a/.jenkins b/.jenkins index 1957558cb7..6f9ff27bb2 100644 --- a/.jenkins +++ b/.jenkins @@ -2,7 +2,7 @@ pipeline { options { disableConcurrentBuilds(abortPrevious: true) newContainerPerStage() - timeout(time: 2, unit: 'HOURS') + timeout(time: 7, unit: 'HOURS') } triggers { issueCommentTrigger('.*do: test') diff --git a/scripts/cmake-presets/ci-ubuntu-github.json b/scripts/cmake-presets/ci-ubuntu-github.json index 0363fa4650..49e1cde457 100644 --- a/scripts/cmake-presets/ci-ubuntu-github.json +++ b/scripts/cmake-presets/ci-ubuntu-github.json @@ -57,6 +57,17 @@ "CELERITAS_USE_JSON": {"type": "BOOL", "value": "ON"} } }, + { + "name": "ultralite", + "inherits": ["base"], + "displayName": "ultralite build with no externals", + "cacheVariables": { + "BUILD_TESTING": {"type": "BOOL", "value": "ON"}, + "CELERITAS_DEBUG": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILD_DEMOS": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "OFF"} + } + }, { "name": ".vecgeom", "hidden": true, diff --git a/scripts/cmake-presets/ci-windows-github.json b/scripts/cmake-presets/ci-windows-github.json new file mode 100644 index 0000000000..0b05410583 --- /dev/null +++ b/scripts/cmake-presets/ci-windows-github.json @@ -0,0 +1,72 @@ +{ + "version": 3, + "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, + "configurePresets": [ + { + "name": "base", + "displayName": "windows options for MSVC", + "inherits": ["default"], + "binaryDir": "${sourceDir}/build", + "generator": "Ninja", + "cacheVariables": { + "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILD_DEMOS": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "ON"}, + "CELERITAS_BUILD_DOCS": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_DEBUG": {"type": "BOOL", "value": "ON"}, + "CELERITAS_TEST_VERBOSE":{"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_CUDA": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_Geant4": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_HIP": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_JSON": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_MPI": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_SWIG": {"type": "BOOL", "value": "OFF"}, + "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"}, + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_CXX_COMPILER_LAUNCHER": "ccache", + "CMAKE_CXX_EXTENSIONS": {"type": "BOOL", "value": "OFF"}, + "CMAKE_CXX_FLAGS": "/W2 /WX", + "CMAKE_CXX_STANDARD": "17", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install" + } + }, + { + "name": "ultralite", + "inherits": ["base"], + "displayName": "ultralite build with no externals", + "cacheVariables": { + "BUILD_TESTING": {"type": "BOOL", "value": "ON"}, + "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "OFF"} + } + } + ], + "buildPresets": [ + { + "name": "ultralite", + "configurePreset": "ultralite", + "nativeToolOptions": ["-k0"], + "verbose": true, + "jobs": 4 + } + ], + "testPresets": [ + { + "name": "ultralite", + "configurePreset": "ultralite", + "execution": { + "noTestsAction": "error", + "stopOnFailure": false, + "jobs": 16, + "timeout": 180 + }, + "output": { + "maxFailedTestOutputSize": 1048576, + "maxPassedTestOutputSize": 65536, + "outputOnFailure": true + } + } + ] +} diff --git a/scripts/cmake-presets/crusher.json b/scripts/cmake-presets/crusher.json deleted file mode 100644 index 61a6ee88c4..0000000000 --- a/scripts/cmake-presets/crusher.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "version": 3, - "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, - "configurePresets": [ - { - "name": ".base", - "hidden": true, - "inherits": ["full"], - "binaryDir": "${sourceDir}/build-${presetName}", - "generator": "Ninja", - "cacheVariables": { - "CELERITAS_BUILD_DOCS": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_Geant4": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_HIP": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_JSON": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_MPI": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_SWIG": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_TEST_RESOURCE_LOCK": {"type": "BOOL", "value": "ON"}, - "CMAKE_HIP_ARCHITECTURES": {"type": "STRING", "value": "gfx90a"}, - "CMAKE_HIP_FLAGS": "-munsafe-fp-atomics", - "CMAKE_EXE_LINKER_FLAGS": "-Wno-unused-command-line-argument", - "CMAKE_HIP_FLAGS_DEBUG": "-g -ggdb -O", - "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG -march=znver3 -mtune=znver3", - "CMAKE_CXX_STANDARD": "17", - "CMAKE_CXX_EXTENSIONS": {"type": "BOOL", "value": "OFF"}, - "CMAKE_EXPORT_COMPILE_COMMANDS": {"type": "BOOL", "value": "ON"} - } - }, - { - "name": "base", - "displayName": "Crusher default options (GCC, debug)", - "inherits": [".base"], - "binaryDir": "${sourceDir}/build" - }, - { - "name": "mini", - "displayName": "Crusher default options (GCC, debug)", - "inherits": [".base"], - "binaryDir": "${sourceDir}/build-minimal", - "cacheVariables": { - "CELERITAS_BUILD_DOCS": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_Geant4": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_HIP": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_JSON": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_MPI": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_SWIG": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"}, - "CMAKE_HIP_ARCHITECTURES": {"type": "STRING", "value": "gfx90a"}, - "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG -march=znver3 -mtune=znver3", - "CMAKE_EXPORT_COMPILE_COMMANDS": {"type": "BOOL", "value": "ON"} - } - }, - { - "name": "ndebug", - "displayName": "Crusher release mode", - "inherits": [".ndebug", ".base"], - "cacheVariables": { - "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "OFF"} - } - }, - { - "name": "ndebug-serial", - "displayName": "Crusher release mode (no openmp)", - "inherits": [".ndebug", ".base"], - "cacheVariables": { - "CELERITAS_BUILD_TESTS": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "OFF"}, - "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "OFF"} - } - } - ], - "buildPresets": [ - { - "name": "base", - "configurePreset": "base", - "jobs": 8, - "nativeToolOptions": ["-k0"] - }, - {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"} - ], - "testPresets": [ - { - "name": "base", - "configurePreset": "base", - "output": {"outputOnFailure": true}, - "execution": {"noTestsAction": "error", "stopOnFailure": false, "jobs": 8} - }, - {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"} - ] -} diff --git a/scripts/cmake-presets/summit.json b/scripts/cmake-presets/summit.json deleted file mode 100644 index 962a97db28..0000000000 --- a/scripts/cmake-presets/summit.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "version": 3, - "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, - "configurePresets": [ - { - "name": ".base", - "hidden": true, - "inherits": ["full"], - "binaryDir": "$env{PROJWORK}/csc404/celeritas/build-${presetName}", - "generator": "Ninja", - "cacheVariables": { - "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_BUILD_DOCS": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_OpenMP": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_Geant4": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_HepMC3": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_CUDA": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_HIP": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_JSON": {"type": "BOOL", "value": "ON"}, - "CELERITAS_USE_MPI": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_ROOT": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_SWIG": {"type": "BOOL", "value": "OFF"}, - "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "ON"}, - "CELERITAS_TEST_RESOURCE_LOCK": {"type": "BOOL", "value": "ON"}, - "CMAKE_CXX_FLAGS": "-Wall -Wextra -Wno-psabi -Werror -pedantic -pedantic-errors", - "CMAKE_CUDA_FLAGS": "-Werror all-warnings -Xcompiler -Wno-psabi", - "CMAKE_CUDA_ARCHITECTURES": {"type": "STRING", "value": "70"}, - "CMAKE_CXX_STANDARD": {"type": "STRING", "value": "17"}, - "CMAKE_CXX_EXTENSIONS": {"type": "BOOL", "value": "OFF"}, - "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG -mcpu=powerpc64le -mtune=powerpc64le", - "CMAKE_EXPORT_COMPILE_COMMANDS": {"type": "BOOL", "value": "ON"}, - "CMAKE_INSTALL_PREFIX": "$env{PROJWORK}/csc404/celeritas/install-${presetName}" - } - }, - { - "name": "base", - "displayName": "Summit default options (GCC, debug)", - "inherits": [".debug", ".base"], - "binaryDir": "${sourceDir}/build" - }, - { - "name": "reldeb-novg", - "displayName": "Summit release mode", - "inherits": [".reldeb", ".base"], - "cacheVariables": { - "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"} - } - }, - { - "name": "reldeb", - "displayName": "Summit release mode", - "inherits": [".reldeb", ".base"] - }, - { - "name": "ndebug-single", - "displayName": "Summit with no vecgeom and single precision", - "inherits": [".ndebug", ".base"], - "cacheVariables": { - "CELERITAS_REAL_TYPE": "float", - "CMAKE_CXX_FLAGS": "-Wall -Wno-psabi -Wno-narrowing -Wextra -pedantic", - "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"} - } - }, - { - "name": "ndebug-novg", - "displayName": "Summit release mode", - "inherits": [".ndebug", ".base"], - "cacheVariables": { - "CELERITAS_USE_VecGeom": {"type": "BOOL", "value": "OFF"} - } - }, - { - "name": "ndebug", - "displayName": "Summit release mode", - "inherits": [".ndebug", ".base"] - } - ], - "buildPresets": [ - { - "name": "base", - "configurePreset": "base", - "jobs": 8, - "nativeToolOptions": ["-k0"] - }, - {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"}, - {"name": "ndebug-novg", "configurePreset": "ndebug-novg", "inherits": "base"}, - {"name": "reldeb", "configurePreset": "reldeb", "inherits": "base"}, - {"name": "reldeb-novg", "configurePreset": "reldeb-novg", "inherits": "base"} - ], - "testPresets": [ - { - "name": "base", - "configurePreset": "base", - "output": {"outputOnFailure": true}, - "execution": {"noTestsAction": "error", "stopOnFailure": false, "jobs": 8} - }, - {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"}, - {"name": "ndebug-novg", "configurePreset": "ndebug-novg", "inherits": "base"}, - {"name": "reldeb", "configurePreset": "reldeb", "inherits": "base"}, - {"name": "reldeb-novg", "configurePreset": "reldeb-novg", "inherits": "base"} - ] -} diff --git a/scripts/requirements.txt b/scripts/doc-requirements.txt similarity index 71% rename from scripts/requirements.txt rename to scripts/doc-requirements.txt index 22c87b37b9..ea1268588b 100644 --- a/scripts/requirements.txt +++ b/scripts/doc-requirements.txt @@ -1,4 +1,4 @@ -Sphinx >= 4, < 7.3 +Sphinx >= 4, ~= 7.3.0 breathe furo sphinxcontrib-bibtex diff --git a/scripts/env/crusher.sh b/scripts/env/crusher.sh deleted file mode 100755 index a39a77ce7c..0000000000 --- a/scripts/env/crusher.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh -e - -_celer_base=$PROJWORK/csc404/celeritas-crusher - -# From %clang@14.0.0-rocm5.6.0 in OLCF's spack compilers -# but upgraded to newer amd -module load PrgEnv-amd/8.3.3 amd/5.6.0 craype-x86-trento libfabric \ - cray-pmi/6.1.2 cray-python/3.9.13.1 -export RFE_811452_DISABLE=1 -export LD_LIBRARY_PATH=/opt/cray/pe/pmi/6.1.2/lib:$LD_LIBRARY_PATH:/opt/cray/pe/gcc-libs:/opt/cray/libfabric/1.15.0.0/lib64 -export LIBRARY_PATH=/opt/rocm-5.6.0/lib:$LIBRARY_PATH - -# Avoid linking multiple different libsci (one with openmp, one without) -module unload cray-libsci - -# Set up compilers -test -n "${CRAYPE_DIR}" -export CXX=${CRAYPE_DIR}/bin/CC -export CC=${CRAYPE_DIR}/bin/cc - -# Do NOT load the accelerator target, because it adds -# -fopenmp-targets=amdgcn-amd-amdhsa -# -Xopenmp-target=amdgcn-amd-amdhsa -march=gfx90a -# which implicitly defines __CUDA_ARCH__ -# module load craype-accel-amd-gfx90a - -# Set up celeritas -export SPACK_ROOT=/ccs/proj/csc404/spack-crusher -export PATH=${_celer_base}/spack/view/bin:$PATH -export CMAKE_PREFIX_PATH=${_celer_base}/spack/view:${CMAKE_PREFIX_PATH} -export MODULEPATH=${SPACK_ROOT}/share/spack/lmod/cray-sles15-x86_64/Core:${MODULEPATH} - -# Set up Geant4 data -module load geant4-data -test -n "${G4ENSDFSTATEDATA}" -test -e "${G4ENSDFSTATEDATA}" - -# Make llvm available -test -n "${ROCM_PATH}" -export PATH=$PATH:${ROCM_PATH}/llvm/bin diff --git a/scripts/env/summit.sh b/scripts/env/summit.sh deleted file mode 100755 index 608ec63462..0000000000 --- a/scripts/env/summit.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -e - -_celer_base=$PROJWORK/csc404/celeritas -export LLVM_ROOT=/sw/summit/llvm/14.0.0/14.0.0-0 - -module load cuda/11.4.2 gcc/11.2 spectrum-mpi/10.4 -module unload cmake python -export PATH=${_celer_base}/spack/view/bin:$LLVM_ROOT/bin:$PATH -export CMAKE_PREFIX_PATH=${_celer_base}/spack/view:${CMAKE_PREFIX_PATH} -export MODULEPATH=/ccs/proj/csc404/spack/share/spack/lmod/linux-rhel8-ppc64le/Core:$MODULEPATH -export CXX=${OLCF_GCC_ROOT}/bin/g++ -export CC=${OLCF_GCC_ROOT}/bin/gcc -export CUDACXX=${OLCF_CUDA_ROOT}/bin/nvcc -module load geant4-data From 6f47d218fdc7badf0af12116560c039c9e532dc5 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Thu, 25 Apr 2024 07:49:01 -0400 Subject: [PATCH 19/59] Fix no-JSON and Windows builds (#1200) * Fix when JSON is disabled * Fix windows warnings/errors * Revert "Allow ultralite builds to fail" * Define generic fma * Use cassert * Use fma algorithm and assert that distributions are floating point --- .github/workflows/build-ultralite.yml | 2 -- app/celer-g4/CMakeLists.txt | 2 +- src/celeritas/grid/PolyEvaluator.hh | 3 ++- src/celeritas/optical/OpticalGenData.hh | 2 +- .../optical/ScintillationPreGenerator.hh | 8 +++--- .../random/distribution/NormalDistribution.hh | 3 +++ .../distribution/UniformRealDistribution.hh | 3 +++ src/corecel/io/JsonPimpl.hh | 2 ++ src/corecel/math/Algorithms.hh | 27 +++++++++++++++++++ src/corecel/math/ArrayUtils.hh | 4 +-- src/corecel/sys/ScopedSignalHandler.cc | 7 ++++- src/geocel/rasterize/Image.cc | 3 ++- src/orange/MatrixUtils.cc | 5 ++-- src/orange/MatrixUtils.hh | 5 ++-- .../orangeinp/detail/PostfixLogicBuilder.cc | 3 +++ test/corecel/math/Algorithms.test.cc | 9 +++++++ 16 files changed, 71 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-ultralite.yml b/.github/workflows/build-ultralite.yml index f1bb730a1c..5746120ace 100644 --- a/.github/workflows/build-ultralite.yml +++ b/.github/workflows/build-ultralite.yml @@ -10,7 +10,6 @@ concurrency: jobs: linux: - continue-on-error: true name: ultralite-ubuntu env: CCACHE_DIR: "${{github.workspace}}/.ccache" @@ -63,7 +62,6 @@ jobs: run: | ccache -s windows: - continue-on-error: true name: ultralite-windows env: CCACHE_DIR: "${{github.workspace}}\\.ccache" diff --git a/app/celer-g4/CMakeLists.txt b/app/celer-g4/CMakeLists.txt index 3c0528c1d3..d633e50161 100644 --- a/app/celer-g4/CMakeLists.txt +++ b/app/celer-g4/CMakeLists.txt @@ -39,7 +39,7 @@ else() set(SOURCES celer-g4.nogeant.cc ) - set(LIBRARIES) + set(LIBRARIES Celeritas::corecel) endif() if(CELERITAS_USE_ROOT AND CELERITAS_USE_Geant4) diff --git a/src/celeritas/grid/PolyEvaluator.hh b/src/celeritas/grid/PolyEvaluator.hh index 1c129a5bb5..f2d8241395 100644 --- a/src/celeritas/grid/PolyEvaluator.hh +++ b/src/celeritas/grid/PolyEvaluator.hh @@ -13,6 +13,7 @@ #include "corecel/Macros.hh" #include "corecel/Types.hh" #include "corecel/cont/Array.hh" +#include "corecel/math/Algorithms.hh" namespace celeritas { @@ -82,7 +83,7 @@ class PolyEvaluator template = true> CELER_CONSTEXPR_FUNCTION T calc_impl(T arg) const { - return std::fma(arg, calc_impl(arg), coeffs_[M]); + return fma(arg, calc_impl(arg), coeffs_[M]); } template = true> diff --git a/src/celeritas/optical/OpticalGenData.hh b/src/celeritas/optical/OpticalGenData.hh index 1e37507169..0e50eee316 100644 --- a/src/celeritas/optical/OpticalGenData.hh +++ b/src/celeritas/optical/OpticalGenData.hh @@ -42,7 +42,7 @@ struct OpticalGenParamsData bool cerenkov{false}; //!< Whether Cerenkov is enabled bool scintillation{false}; //!< Whether scintillation is enabled - real_type capacity{0}; //!< Distribution data buffer capacity + size_type capacity{0}; //!< Distribution data buffer capacity //// METHODS //// diff --git a/src/celeritas/optical/ScintillationPreGenerator.hh b/src/celeritas/optical/ScintillationPreGenerator.hh index c6722d77a6..fd47b63965 100644 --- a/src/celeritas/optical/ScintillationPreGenerator.hh +++ b/src/celeritas/optical/ScintillationPreGenerator.hh @@ -117,14 +117,14 @@ ScintillationPreGenerator::operator()(Generator& rng) { real_type sigma = shared_.resolution_scale[optmat_id_] * std::sqrt(mean_num_photons_); - result.num_photons = clamp_to_nonneg( + result.num_photons = static_cast(clamp_to_nonneg( NormalDistribution(mean_num_photons_, sigma)(rng) - + real_type{0.5}); + + real_type{0.5})); } else if (mean_num_photons_ > 0) { - result.num_photons - = PoissonDistribution(mean_num_photons_)(rng); + result.num_photons = static_cast( + PoissonDistribution(mean_num_photons_)(rng)); } if (result.num_photons > 0) diff --git a/src/celeritas/random/distribution/NormalDistribution.hh b/src/celeritas/random/distribution/NormalDistribution.hh index 639398f82c..35124d4ccc 100644 --- a/src/celeritas/random/distribution/NormalDistribution.hh +++ b/src/celeritas/random/distribution/NormalDistribution.hh @@ -8,6 +8,7 @@ #pragma once #include +#include #include "corecel/Assert.hh" #include "corecel/Macros.hh" @@ -35,6 +36,8 @@ namespace celeritas template class NormalDistribution { + static_assert(std::is_floating_point_v); + public: //!@{ //! \name Type aliases diff --git a/src/celeritas/random/distribution/UniformRealDistribution.hh b/src/celeritas/random/distribution/UniformRealDistribution.hh index 8f11bc41bc..d6a325c5db 100644 --- a/src/celeritas/random/distribution/UniformRealDistribution.hh +++ b/src/celeritas/random/distribution/UniformRealDistribution.hh @@ -8,6 +8,7 @@ #pragma once #include +#include #include "corecel/Assert.hh" #include "corecel/Macros.hh" @@ -28,6 +29,8 @@ namespace celeritas template class UniformRealDistribution { + static_assert(std::is_floating_point_v); + public: //!@{ //! \name Type aliases diff --git a/src/corecel/io/JsonPimpl.hh b/src/corecel/io/JsonPimpl.hh index 028ea4b6e7..b142eb2d37 100644 --- a/src/corecel/io/JsonPimpl.hh +++ b/src/corecel/io/JsonPimpl.hh @@ -56,6 +56,8 @@ void to_json_pimpl(JsonPimpl* jp, T const& self) CELER_EXPECT(jp); to_json(jp->obj, self); #else + CELER_DISCARD(jp); + CELER_DISCARD(self); CELER_NOT_CONFIGURED("JSON"); #endif } diff --git a/src/corecel/math/Algorithms.hh b/src/corecel/math/Algorithms.hh index 8064e33ab3..4ece3ff22b 100644 --- a/src/corecel/math/Algorithms.hh +++ b/src/corecel/math/Algorithms.hh @@ -501,6 +501,33 @@ inline CELER_FUNCTION float rsqrt(float value) } #endif +//---------------------------------------------------------------------------// +/*! + * Use fused multiply-add for generic calculations. + * + * This provides a floating point specialization so that \c fma can be used in + * code that is accelerated for floating point calculations but still works + * correctly with integer arithmetic. + * + * Because of the single template parameter, it may be easier to use \c + * std::fma directly in most cases. + */ +template::value, bool> = true> +inline CELER_FUNCTION T fma(T a, T b, T y) +{ + return std::fma(a, b, y); +} + +//---------------------------------------------------------------------------// +/*! + * Provide an FMA-like interface for integers. + */ +template::value, bool> = true> +CELER_CONSTEXPR_FUNCTION T fma(T a, T b, T y) +{ + return a * b + y; +} + //---------------------------------------------------------------------------// /*! * Integer division, rounding up, for positive numbers. diff --git a/src/corecel/math/ArrayUtils.hh b/src/corecel/math/ArrayUtils.hh index 4db5929ffd..c64f17f5f8 100644 --- a/src/corecel/math/ArrayUtils.hh +++ b/src/corecel/math/ArrayUtils.hh @@ -80,7 +80,7 @@ CELER_FUNCTION void axpy(T a, Array const& x, Array* y) CELER_EXPECT(y); for (size_type i = 0; i != N; ++i) { - (*y)[i] = std::fma(a, x[i], (*y)[i]); + (*y)[i] = fma(a, x[i], (*y)[i]); } } @@ -94,7 +94,7 @@ CELER_FUNCTION T dot_product(Array const& x, Array const& y) T result{}; for (size_type i = 0; i != N; ++i) { - result = std::fma(x[i], y[i], result); + result = fma(x[i], y[i], result); } return result; } diff --git a/src/corecel/sys/ScopedSignalHandler.cc b/src/corecel/sys/ScopedSignalHandler.cc index 4799fb0c49..524503aadb 100644 --- a/src/corecel/sys/ScopedSignalHandler.cc +++ b/src/corecel/sys/ScopedSignalHandler.cc @@ -8,6 +8,7 @@ #include "ScopedSignalHandler.hh" #include +#include #include #include @@ -26,7 +27,11 @@ sig_atomic_t volatile g_celer_signal_bits_ = 0; //! Set the bit corresponding to a signal extern "C" void celer_set_signal(int signal) { - CELER_ASSERT(signal >= 0 && signal < static_cast(sizeof(int) * 8 - 1)); + // It's undefined behavior to throw C++ exceptions from inside a C function + // call, so use a C assert to check that the bit being set is within + // bounds. + assert(signal >= 0 && signal < static_cast(sizeof(int) * 8 - 1)); + g_celer_signal_bits_ |= (1 << signal); } diff --git a/src/geocel/rasterize/Image.cc b/src/geocel/rasterize/Image.cc index 15902f2db0..7558caf7b4 100644 --- a/src/geocel/rasterize/Image.cc +++ b/src/geocel/rasterize/Image.cc @@ -74,7 +74,8 @@ ImageParams::ImageParams(ImageInput const& inp) scalars.pixel_width = width_y / num_y; size_type num_x = inp.horizontal_divisor - * std::ceil(width_x / (inp.horizontal_divisor * scalars.pixel_width)); + * static_cast(std::ceil( + width_x / (inp.horizontal_divisor * scalars.pixel_width))); CELER_ASSERT(num_x >= inp.horizontal_divisor); scalars.dims = {num_y, num_x}; diff --git a/src/orange/MatrixUtils.cc b/src/orange/MatrixUtils.cc index 6032a3697a..96a383aac8 100644 --- a/src/orange/MatrixUtils.cc +++ b/src/orange/MatrixUtils.cc @@ -9,6 +9,7 @@ #include +#include "corecel/math/Algorithms.hh" #include "corecel/math/ArrayUtils.hh" #include "corecel/math/SoftEqual.hh" @@ -73,7 +74,7 @@ gemm(SquareMatrix const& a, SquareMatrix const& b) // Accumulate dot products for (size_type k = 0; k != N; ++k) { - result[i][j] = std::fma(b[k][j], a[i][k], result[i][j]); + result[i][j] = fma(b[k][j], a[i][k], result[i][j]); } } } @@ -106,7 +107,7 @@ SquareMatrix gemm(matrix::TransposePolicy, // Accumulate dot products for (size_type k = 0; k != N; ++k) { - result[i][j] = std::fma(b[k][j], a[k][i], result[i][j]); + result[i][j] = fma(b[k][j], a[k][i], result[i][j]); } } } diff --git a/src/orange/MatrixUtils.hh b/src/orange/MatrixUtils.hh index cf41fb5e38..38f3405d2a 100644 --- a/src/orange/MatrixUtils.hh +++ b/src/orange/MatrixUtils.hh @@ -12,6 +12,7 @@ #include "corecel/Macros.hh" #include "corecel/cont/Array.hh" +#include "corecel/math/Algorithms.hh" #include "corecel/math/Turn.hh" #include "geocel/Types.hh" @@ -133,7 +134,7 @@ CELER_FUNCTION Array gemv(T alpha, result[i] = beta * y[i]; for (size_type j = 0; j != N; ++j) { - result[i] = std::fma(alpha, a[i][j] * x[j], result[i]); + result[i] = fma(alpha, a[i][j] * x[j], result[i]); } } return result; @@ -169,7 +170,7 @@ CELER_FUNCTION Array gemv(matrix::TransposePolicy, { for (size_type i = 0; i != N; ++i) { - result[i] = std::fma(alpha, a[j][i] * x[j], result[i]); + result[i] = fma(alpha, a[j][i] * x[j], result[i]); } } return result; diff --git a/src/orange/orangeinp/detail/PostfixLogicBuilder.cc b/src/orange/orangeinp/detail/PostfixLogicBuilder.cc index d770ceda4d..4fd8204fe5 100644 --- a/src/orange/orangeinp/detail/PostfixLogicBuilder.cc +++ b/src/orange/orangeinp/detail/PostfixLogicBuilder.cc @@ -7,6 +7,9 @@ //---------------------------------------------------------------------------// #include "PostfixLogicBuilder.hh" +#include +#include + #include "corecel/cont/VariantUtils.hh" #include "corecel/math/Algorithms.hh" #include "orange/OrangeTypes.hh" diff --git a/test/corecel/math/Algorithms.test.cc b/test/corecel/math/Algorithms.test.cc index 2d37631216..16595e7ec7 100644 --- a/test/corecel/math/Algorithms.test.cc +++ b/test/corecel/math/Algorithms.test.cc @@ -325,6 +325,15 @@ TEST(MathTest, rsqrt) //---------------------------------------------------------------------------// +TEST(MathTest, fma) +{ + EXPECT_DOUBLE_EQ(std::fma(1.0, 2.0, 8.0), fma(1.0, 2.0, 8.0)); + + EXPECT_DOUBLE_EQ(1 * 2 + 8, fma(1, 2, 8)); +} + +//---------------------------------------------------------------------------// + TEST(MathTest, ceil_div) { EXPECT_EQ(0u, ceil_div(0u, 32u)); From f7d7ac9c9d4d8b01be7dc8059702acb5abd56919 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Thu, 25 Apr 2024 07:42:57 -0500 Subject: [PATCH 20/59] Fix missing factor of two in Wentzel OK&VI transport xs (#1202) --- src/celeritas/em/xs/WentzelHelper.hh | 2 + .../em/xs/WentzelTransportXsCalculator.hh | 2 +- test/celeritas/em/CoulombScattering.test.cc | 60 +++++++++---------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/celeritas/em/xs/WentzelHelper.hh b/src/celeritas/em/xs/WentzelHelper.hh index cc58eb901c..be9d92155c 100644 --- a/src/celeritas/em/xs/WentzelHelper.hh +++ b/src/celeritas/em/xs/WentzelHelper.hh @@ -125,6 +125,8 @@ CELER_FUNCTION real_type WentzelHelper::calc_xs_ratio() const //---------------------------------------------------------------------------// /*! * Calculate the Moliere screening coefficient as in [PRM] eqn 8.51. + * + * \note The \c screenZ in Geant4 is equal to twice the screening coefficient. */ CELER_FUNCTION real_type WentzelHelper::calc_screening_coefficient( ParticleTrackView const& particle) const diff --git a/src/celeritas/em/xs/WentzelTransportXsCalculator.hh b/src/celeritas/em/xs/WentzelTransportXsCalculator.hh index 49eedb0f48..fa8e92b0b8 100644 --- a/src/celeritas/em/xs/WentzelTransportXsCalculator.hh +++ b/src/celeritas/em/xs/WentzelTransportXsCalculator.hh @@ -80,7 +80,7 @@ CELER_FUNCTION WentzelTransportXsCalculator::WentzelTransportXsCalculator( ParticleTrackView const& particle, WentzelHelper const& helper) : z_(helper.atomic_number()) - , screening_coeff_(helper.screening_coefficient()) + , screening_coeff_(2 * helper.screening_coefficient()) , costheta_max_elec_(helper.costheta_max_electron()) , beta_sq_(particle.beta_sq()) , xs_factor_(this->calc_xs_factor(particle)) diff --git a/test/celeritas/em/CoulombScattering.test.cc b/test/celeritas/em/CoulombScattering.test.cc index d668c43659..0ae3c37f63 100644 --- a/test/celeritas/em/CoulombScattering.test.cc +++ b/test/celeritas/em/CoulombScattering.test.cc @@ -241,40 +241,40 @@ TEST_F(CoulombScatteringTest, wokvi_transport_xs) xs.push_back(calc_transport_xs(costheta_max) / units::barn); } } - static double const expected_xs[] = {0.19516611768754, - 0.19475734301371, - 0.19307107844035, - 0.18826457727067, - 0.18210234522701, - 0.14849151789768, + static double const expected_xs[] = {0.18738907324438, + 0.18698029857321, + 0.18529403401504, + 0.1804875329214, + 0.17432530107014, + 0.14071448472406, 0, - 0.052798367939378, - 0.052695669889626, - 0.052271981881786, - 0.051064284057599, - 0.049515930355632, - 0.041070671779723, + 0.050844259956663, + 0.050741561907078, + 0.050317873900199, + 0.049110176080819, + 0.047561822391017, + 0.039116564509577, 0, - 0.002472275627657, - 0.0024681511259515, - 0.002451134671678, - 0.0024026299507655, - 0.0023404433473951, - 0.0020012556180187, + 0.00239379259103, + 0.0023896680893247, + 0.0023726516350529, + 0.0023241469141481, + 0.0022619603107973, + 0.0019227725825426, 0, - 3.4837670176241e-07, - 3.4796383511025e-07, - 3.4626046917181e-07, - 3.4140509151026e-07, - 3.3518014131324e-07, - 3.0122705954759e-07, + 3.4052045960474e-07, + 3.4010759295258e-07, + 3.3840422701414e-07, + 3.3354884935259e-07, + 3.2732389915557e-07, + 2.9337081738993e-07, 0, - 3.988372624941e-09, - 3.9842439204449e-09, - 3.9672101043866e-09, - 3.9186558811768e-09, - 3.8564058066393e-09, - 3.516871865997e-09, + 3.9098094802963e-09, + 3.9056807758002e-09, + 3.8886469597418e-09, + 3.8400927365321e-09, + 3.7778426619946e-09, + 3.4383087213522e-09, 0}; EXPECT_VEC_SOFT_EQ(expected_xs, xs); } From 9c64b46f80b119d02b4da6103748346293ef50fd Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Thu, 25 Apr 2024 09:22:37 -0400 Subject: [PATCH 21/59] Make "output interface" streamable (#1203) * Make standalone output streamable and include metadata * Update tests --- src/corecel/io/OutputInterface.cc | 25 +++++++++++++++++--- src/corecel/io/OutputInterface.hh | 4 ++++ test/celeritas/geo/Geometry.test.cc | 6 ++--- test/celeritas/global/ActionRegistry.test.cc | 2 +- test/celeritas/mat/Material.test.cc | 2 +- test/celeritas/phys/Particle.test.cc | 2 +- test/celeritas/phys/Physics.test.cc | 2 +- test/geocel/g4/GeantGeo.test.cc | 2 +- test/geocel/vg/Vecgeom.test.cc | 4 ++-- test/orange/Orange.test.cc | 10 ++++---- 10 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/corecel/io/OutputInterface.cc b/src/corecel/io/OutputInterface.cc index d6d1a0e8bd..3f705396e0 100644 --- a/src/corecel/io/OutputInterface.cc +++ b/src/corecel/io/OutputInterface.cc @@ -7,6 +7,8 @@ //---------------------------------------------------------------------------// #include "OutputInterface.hh" +#include + #include "celeritas_config.h" #include "EnumStringMapper.hh" @@ -36,18 +38,35 @@ char const* to_cstring(Category value) //---------------------------------------------------------------------------// /*! - * Get the JSON representation of a single output (mostly for testing). + * Get the JSON representation of a single output. + * + * This is used mostly for testing, but it can also be useful for quickly + * generating useful JSON output from applications, e.g. with exception output. */ std::string to_string(OutputInterface const& output) +{ + std::ostringstream os; + os << output; + return std::move(os).str(); +} + +//---------------------------------------------------------------------------// +/*! + * Stream the JSON representation of a single output. + */ +std::ostream& operator<<(std::ostream& os, OutputInterface const& output) { #if CELERITAS_USE_JSON JsonPimpl json_wrap; output.output(&json_wrap); - return json_wrap.obj.dump(); + json_wrap.obj["_category"] = to_cstring(output.category()); + json_wrap.obj["_label"] = output.label(); + os << json_wrap.obj; #else CELER_DISCARD(output); - return "\"output unavailable\""; + os << "\"output unavailable\""; #endif + return os; } //---------------------------------------------------------------------------// diff --git a/src/corecel/io/OutputInterface.hh b/src/corecel/io/OutputInterface.hh index 7e266ab054..b7c19ed33a 100644 --- a/src/corecel/io/OutputInterface.hh +++ b/src/corecel/io/OutputInterface.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include // IWYU pragma: export #include // IWYU pragma: export namespace celeritas @@ -61,5 +62,8 @@ char const* to_cstring(OutputInterface::Category value); // Get the JSON representation of a single output (mostly for testing) std::string to_string(OutputInterface const& output); +// Stream the JSON representation of a single output +std::ostream& operator<<(std::ostream&, OutputInterface const& output); + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/test/celeritas/geo/Geometry.test.cc b/test/celeritas/geo/Geometry.test.cc index 7e3fc486f8..86bfb18c17 100644 --- a/test/celeritas/geo/Geometry.test.cc +++ b/test/celeritas/geo/Geometry.test.cc @@ -275,20 +275,20 @@ TEST_F(SimpleCmsTest, output) else if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_VECGEOM) { EXPECT_JSON_EQ( - R"json({"bbox":[[-1000.001,-1000.001,-2000.001],[1000.001,1000.001,2000.001]],"supports_safety":true,"volumes":{"label":["vacuum_tube","si_tracker","em_calorimeter","had_calorimeter","sc_solenoid","fe_muon_chambers","world"]}})json", + R"json({"_category":"internal","_label":"geometry","bbox":[[-1000.001,-1000.001,-2000.001],[1000.001,1000.001,2000.001]],"supports_safety":true,"volumes":{"label":["vacuum_tube","si_tracker","em_calorimeter","had_calorimeter","sc_solenoid","fe_muon_chambers","world"]}})json", to_string(out)); } else if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE && CELERITAS_USE_GEANT4) { EXPECT_JSON_EQ( - R"json({"bbox":[[-1000.0,-1000.0,-2000.0],[1000.0,1000.0,2000.0]],"supports_safety":false,"surfaces":{"label":["world_box@mx","world_box@px","world_box@my","world_box@py","world_box@mz","world_box@pz","crystal_em_calorimeter@excluded.mz","crystal_em_calorimeter@excluded.pz","lhc_vacuum_tube@cz","crystal_em_calorimeter@excluded.cz","crystal_em_calorimeter@interior.cz","hadron_calorimeter@interior.cz","iron_muon_chambers@excluded.cz","iron_muon_chambers@interior.cz"]},"volumes":{"label":["[EXTERIOR]@world0x0","vacuum_tube@0x0","si_tracker@0x0","em_calorimeter@0x0","had_calorimeter@0x0","sc_solenoid@0x0","fe_muon_chambers@0x0","world@0x0"]}})json", + R"json({"_category":"internal","_label":"geometry","bbox":[[-1000.0,-1000.0,-2000.0],[1000.0,1000.0,2000.0]],"supports_safety":false,"surfaces":{"label":["world_box@mx","world_box@px","world_box@my","world_box@py","world_box@mz","world_box@pz","crystal_em_calorimeter@excluded.mz","crystal_em_calorimeter@excluded.pz","lhc_vacuum_tube@cz","crystal_em_calorimeter@excluded.cz","crystal_em_calorimeter@interior.cz","hadron_calorimeter@interior.cz","iron_muon_chambers@excluded.cz","iron_muon_chambers@interior.cz"]},"volumes":{"label":["[EXTERIOR]@world0x0","vacuum_tube@0x0","si_tracker@0x0","em_calorimeter@0x0","had_calorimeter@0x0","sc_solenoid@0x0","fe_muon_chambers@0x0","world@0x0"]}})json", this->genericize_pointers(to_string(out))); } else if (CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE) { EXPECT_JSON_EQ( - R"json({"bbox":[[-1000.0,-1000.0,-2000.0],[1000.0,1000.0,2000.0]],"supports_safety":false,"surfaces":{"label":["world_box.mx@global","world_box.px@global","world_box.my@global","world_box.py@global","world_box.mz@global","world_box.pz@global","guide_tube.coz@global","crystal_em_calorimeter_outer.mz@global","crystal_em_calorimeter_outer.pz@global","silicon_tracker_outer.coz@global","crystal_em_calorimeter_outer.coz@global","hadron_calorimeter_outer.coz@global","superconducting_solenoid_outer.coz@global","iron_muon_chambers_outer.coz@global"]},"volumes":{"label":["[EXTERIOR]@global","vacuum_tube@global","si_tracker@global","em_calorimeter@global","had_calorimeter@global","sc_solenoid@global","fe_muon_chambers@global","world@global"]}})json", + R"json({"_category":"internal","_label":"geometry","bbox":[[-1000.0,-1000.0,-2000.0],[1000.0,1000.0,2000.0]],"supports_safety":false,"surfaces":{"label":["world_box.mx@global","world_box.px@global","world_box.my@global","world_box.py@global","world_box.mz@global","world_box.pz@global","guide_tube.coz@global","crystal_em_calorimeter_outer.mz@global","crystal_em_calorimeter_outer.pz@global","silicon_tracker_outer.coz@global","crystal_em_calorimeter_outer.coz@global","hadron_calorimeter_outer.coz@global","superconducting_solenoid_outer.coz@global","iron_muon_chambers_outer.coz@global"]},"volumes":{"label":["[EXTERIOR]@global","vacuum_tube@global","si_tracker@global","em_calorimeter@global","had_calorimeter@global","sc_solenoid@global","fe_muon_chambers@global","world@global"]}})json", this->genericize_pointers(to_string(out))); } } diff --git a/test/celeritas/global/ActionRegistry.test.cc b/test/celeritas/global/ActionRegistry.test.cc index 76b4ed39f5..e782398cb6 100644 --- a/test/celeritas/global/ActionRegistry.test.cc +++ b/test/celeritas/global/ActionRegistry.test.cc @@ -138,7 +138,7 @@ TEST_F(ActionRegistryTest, output) if (CELERITAS_USE_JSON) { EXPECT_JSON_EQ( - R"json({"description":["","explicit action test","the second implicit action"],"label":["impl1","explicit","impl2"]})json", + R"json({"_category":"internal","_label":"actions","description":["","explicit action test","the second implicit action"],"label":["impl1","explicit","impl2"]})json", to_string(out)); } } diff --git a/test/celeritas/mat/Material.test.cc b/test/celeritas/mat/Material.test.cc index f019af79a6..290fd34b89 100644 --- a/test/celeritas/mat/Material.test.cc +++ b/test/celeritas/mat/Material.test.cc @@ -315,7 +315,7 @@ TEST_F(MaterialTest, output) if (CELERITAS_USE_JSON && CELERITAS_UNITS == CELERITAS_UNITS_CGS) { EXPECT_JSON_EQ( - R"json({"_units":{"atomic_mass":"amu","mean_excitation_energy":"MeV","nuclear_mass":"MeV/c^2"},"elements":{"atomic_mass":[1.008,26.9815385,22.98976928,126.90447],"atomic_number":[1,13,11,53],"coulomb_correction":[6.400821803338426e-05,0.010734632775699565,0.00770256745342534,0.15954439947436763],"isotope_fractions":[[0.9,0.1],[0.7,0.3],[1.0],[0.05,0.15,0.8]],"isotope_ids":[[0,1],[2,3],[4],[5,6,7]],"label":["H","Al","Na","I"],"mass_radiation_coeff":[0.0158611264432063,0.04164723292591279,0.03605392839455309,0.11791841505608874]},"isotopes":{"atomic_mass_number":[1,2,27,28,23,125,126,127],"atomic_number":[1,1,13,13,11,53,53,53],"label":["1H","2H","27Al","28Al","23Na","125I","126I","127I"],"nuclear_mass":[938.272,1875.61,25126.5,26058.3,21409.2,116321.0,117253.0,118184.0]},"materials":{"density":[3.6700020622594716,0.0,0.00017976000000000003,0.00017943386624303615],"electron_density":[9.4365282069664e+23,0.0,1.073948435904467e+20,1.072e+20],"element_frac":[[0.5,0.5],[],[1.0],[1.0]],"element_id":[[2,3],[],[0],[0]],"label":["NaI","hard vacuum","H2@1","H2@2"],"matter_state":["solid","unspecified","gas","gas"],"mean_excitation_energy":[0.00040000760709482647,0.0,1.9199999999999986e-05,1.9199999999999986e-05],"number_density":[2.948915064677e+22,0.0,1.073948435904467e+20,1.072e+20],"radiation_length":[3.5393292693170424,null,350729.99844063615,351367.4750467326],"temperature":[293.0,0.0,100.0,110.0],"zeff":[32.0,0.0,1.0,1.0]}})json", + R"json({"_category":"internal","_label":"material","_units":{"atomic_mass":"amu","mean_excitation_energy":"MeV","nuclear_mass":"MeV/c^2"},"elements":{"atomic_mass":[1.008,26.9815385,22.98976928,126.90447],"atomic_number":[1,13,11,53],"coulomb_correction":[6.400821803338426e-05,0.010734632775699565,0.00770256745342534,0.15954439947436763],"isotope_fractions":[[0.9,0.1],[0.7,0.3],[1.0],[0.05,0.15,0.8]],"isotope_ids":[[0,1],[2,3],[4],[5,6,7]],"label":["H","Al","Na","I"],"mass_radiation_coeff":[0.0158611264432063,0.04164723292591279,0.03605392839455309,0.11791841505608874]},"isotopes":{"atomic_mass_number":[1,2,27,28,23,125,126,127],"atomic_number":[1,1,13,13,11,53,53,53],"label":["1H","2H","27Al","28Al","23Na","125I","126I","127I"],"nuclear_mass":[938.272,1875.61,25126.5,26058.3,21409.2,116321.0,117253.0,118184.0]},"materials":{"density":[3.6700020622594716,0.0,0.00017976000000000003,0.00017943386624303615],"electron_density":[9.4365282069664e+23,0.0,1.073948435904467e+20,1.072e+20],"element_frac":[[0.5,0.5],[],[1.0],[1.0]],"element_id":[[2,3],[],[0],[0]],"label":["NaI","hard vacuum","H2@1","H2@2"],"matter_state":["solid","unspecified","gas","gas"],"mean_excitation_energy":[0.00040000760709482647,0.0,1.9199999999999986e-05,1.9199999999999986e-05],"number_density":[2.948915064677e+22,0.0,1.073948435904467e+20,1.072e+20],"radiation_length":[3.5393292693170424,null,350729.99844063615,351367.4750467326],"temperature":[293.0,0.0,100.0,110.0],"zeff":[32.0,0.0,1.0,1.0]}})json", to_string(out)); } } diff --git a/test/celeritas/phys/Particle.test.cc b/test/celeritas/phys/Particle.test.cc index 8c158cf9c9..5244d27702 100644 --- a/test/celeritas/phys/Particle.test.cc +++ b/test/celeritas/phys/Particle.test.cc @@ -94,7 +94,7 @@ TEST_F(ParticleTest, output) if (CELERITAS_USE_JSON && CELERITAS_UNITS == CELERITAS_UNITS_CGS) { EXPECT_JSON_EQ( - R"json({"_units":{"charge":"e","mass":"MeV/c^2"},"charge":[-1.0,0.0,0.0,1.0],"decay_constant":[0.0,0.0,0.0011371389583807142,0.0],"is_antiparticle":[false,false,false,true],"label":["electron","gamma","neutron","positron"],"mass":[0.5109989461,0.0,939.565413,0.5109989461],"pdg":[11,22,2112,-11]})json", + R"json({"_category":"internal","_label":"particle","_units":{"charge":"e","mass":"MeV/c^2"},"charge":[-1.0,0.0,0.0,1.0],"decay_constant":[0.0,0.0,0.0011371389583807142,0.0],"is_antiparticle":[false,false,false,true],"label":["electron","gamma","neutron","positron"],"mass":[0.5109989461,0.0,939.565413,0.5109989461],"pdg":[11,22,2112,-11]})json", to_string(out)); } } diff --git a/test/celeritas/phys/Physics.test.cc b/test/celeritas/phys/Physics.test.cc index a74549664a..bfa9f5ec1c 100644 --- a/test/celeritas/phys/Physics.test.cc +++ b/test/celeritas/phys/Physics.test.cc @@ -145,7 +145,7 @@ TEST_F(PhysicsParamsTest, output) GTEST_SKIP() << "JSON required to test output"; } EXPECT_JSON_EQ( - R"json({"models":{"label":["mock-model-1","mock-model-2","mock-model-3","mock-model-4","mock-model-5","mock-model-6","mock-model-7","mock-model-8","mock-model-9","mock-model-10","mock-model-11"],"process_id":[0,0,1,2,2,2,3,3,4,4,5]},"options":{"fixed_step_limiter":0.0,"linear_loss_limit":0.01,"lowest_electron_energy":[0.001,"MeV"],"max_step_over_range":0.2,"min_eprime_over_e":0.8,"min_range":0.1},"processes":{"label":["scattering","absorption","purrs","hisses","meows","barks"]},"sizes":{"integral_xs":8,"model_groups":8,"model_ids":11,"process_groups":5,"process_ids":8,"reals":231,"value_grid_ids":89,"value_grids":89,"value_tables":35}})json", + R"json({"_category":"internal","_label":"physics","models":{"label":["mock-model-1","mock-model-2","mock-model-3","mock-model-4","mock-model-5","mock-model-6","mock-model-7","mock-model-8","mock-model-9","mock-model-10","mock-model-11"],"process_id":[0,0,1,2,2,2,3,3,4,4,5]},"options":{"fixed_step_limiter":0.0,"linear_loss_limit":0.01,"lowest_electron_energy":[0.001,"MeV"],"max_step_over_range":0.2,"min_eprime_over_e":0.8,"min_range":0.1},"processes":{"label":["scattering","absorption","purrs","hisses","meows","barks"]},"sizes":{"integral_xs":8,"model_groups":8,"model_ids":11,"process_groups":5,"process_ids":8,"reals":231,"value_grid_ids":89,"value_grids":89,"value_tables":35}})json", to_string(out)); } diff --git a/test/geocel/g4/GeantGeo.test.cc b/test/geocel/g4/GeantGeo.test.cc index cc040b0769..e0697405ea 100644 --- a/test/geocel/g4/GeantGeo.test.cc +++ b/test/geocel/g4/GeantGeo.test.cc @@ -366,7 +366,7 @@ TEST_F(SolidsTest, output) if (CELERITAS_USE_JSON && CELERITAS_UNITS == CELERITAS_UNITS_CGS) { EXPECT_JSON_EQ( - R"json({"bbox":[[-600.0,-300.0,-75.0],[600.0,300.0,75.0]],"supports_safety":true,"volumes":{"label":["","","","","box500","cone1","para1","sphere1","parabol1","trap1","trd1","trd2","","trd3_refl","tube100","boolean1","polycone1","genPocone1","ellipsoid1","tetrah1","orb1","polyhedr1","hype1","elltube1","ellcone1","arb8b","arb8a","xtru1","World","trd3_refl"]}})json", + R"json({"_category":"internal","_label":"geometry","bbox":[[-600.0,-300.0,-75.0],[600.0,300.0,75.0]],"supports_safety":true,"volumes":{"label":["","","","","box500","cone1","para1","sphere1","parabol1","trap1","trd1","trd2","","trd3_refl","tube100","boolean1","polycone1","genPocone1","ellipsoid1","tetrah1","orb1","polyhedr1","hype1","elltube1","ellcone1","arb8b","arb8a","xtru1","World","trd3_refl"]}})json", to_string(out)); } } diff --git a/test/geocel/vg/Vecgeom.test.cc b/test/geocel/vg/Vecgeom.test.cc index 461a3281a6..489ee2d50e 100644 --- a/test/geocel/vg/Vecgeom.test.cc +++ b/test/geocel/vg/Vecgeom.test.cc @@ -709,7 +709,7 @@ TEST_F(SolidsTest, output) auto out_str = this->genericize_pointers(to_string(out)); EXPECT_JSON_EQ( - R"json({"bbox":[[-600.001,-300.001,-75.001],[600.001,300.001,75.001]],"supports_safety":true,"volumes":{"label":["b500","b100","union1","b100","box500","cone1","para1","sphere1","parabol1","trap1","trd1","trd2","trd3","trd3_refl","tube100","boolean1","polycone1","genPocone1","ellipsoid1","tetrah1","orb1","polyhedr1","hype1","elltube1","ellcone1","arb8b","arb8a","xtru1","World","","trd3_refl"]}})json", + R"json({"_category":"internal","_label":"geometry","bbox":[[-600.001,-300.001,-75.001],[600.001,300.001,75.001]],"supports_safety":true,"volumes":{"label":["b500","b100","union1","b100","box500","cone1","para1","sphere1","parabol1","trap1","trd1","trd2","trd3","trd3_refl","tube100","boolean1","polycone1","genPocone1","ellipsoid1","tetrah1","orb1","polyhedr1","hype1","elltube1","ellcone1","arb8b","arb8a","xtru1","World","","trd3_refl"]}})json", out_str); } } @@ -1264,7 +1264,7 @@ TEST_F(SolidsGeantTest, output) auto out_str = this->genericize_pointers(to_string(out)); EXPECT_JSON_EQ( - R"json({"bbox":[[-600.001,-300.001,-75.001],[600.001,300.001,75.001]],"supports_safety":true,"volumes":{"label":["box500@0x0","cone1@0x0","para1@0x0","sphere1@0x0","parabol1@0x0","trap1@0x0","trd1@0x0","trd2@0x0","trd3@0x0","trd3_refl@0x0","tube100@0x0","","","","","boolean1@0x0","polycone1@0x0","genPocone1@0x0","ellipsoid1@0x0","tetrah1@0x0","orb1@0x0","polyhedr1@0x0","hype1@0x0","elltube1@0x0","ellcone1@0x0","arb8b@0x0","arb8a@0x0","xtru1@0x0","World@0x0","","trd3@0x0_refl"]}})json", + R"json({"_category":"internal","_label":"geometry","bbox":[[-600.001,-300.001,-75.001],[600.001,300.001,75.001]],"supports_safety":true,"volumes":{"label":["box500@0x0","cone1@0x0","para1@0x0","sphere1@0x0","parabol1@0x0","trap1@0x0","trd1@0x0","trd2@0x0","trd3@0x0","trd3_refl@0x0","tube100@0x0","","","","","boolean1@0x0","polycone1@0x0","genPocone1@0x0","ellipsoid1@0x0","tetrah1@0x0","orb1@0x0","polyhedr1@0x0","hype1@0x0","elltube1@0x0","ellcone1@0x0","arb8b@0x0","arb8a@0x0","xtru1@0x0","World@0x0","","trd3@0x0_refl"]}})json", out_str); } } diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index 33826d681f..16e2e6b3aa 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -685,7 +685,7 @@ TEST_F(UniversesTest, TEST_IF_CELERITAS_DOUBLE(output)) if (CELERITAS_USE_JSON) { EXPECT_JSON_EQ( - R"json({"scalars":{"max_depth":3,"max_faces":14,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":12,"inner_nodes":6,"leaf_nodes":9,"local_volume_ids":12},"connectivity_records":25,"daughters":3,"local_surface_ids":55,"local_volume_ids":21,"logic_ints":171,"real_ids":25,"reals":24,"rect_arrays":0,"simple_units":3,"surface_types":25,"transforms":3,"universe_indices":3,"universe_types":3,"volume_records":12}})json", + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":14,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":12,"inner_nodes":6,"leaf_nodes":9,"local_volume_ids":12},"connectivity_records":25,"daughters":3,"local_surface_ids":55,"local_volume_ids":21,"logic_ints":171,"real_ids":25,"reals":24,"rect_arrays":0,"simple_units":3,"surface_types":25,"transforms":3,"universe_indices":3,"universe_types":3,"volume_records":12}})json", to_string(out)); } } @@ -1145,7 +1145,7 @@ TEST_F(HexArrayTest, TEST_IF_CELERITAS_DOUBLE(output)) if (CELERITAS_USE_JSON) { EXPECT_JSON_EQ( - R"json({"scalars":{"max_depth":3,"max_faces":9,"max_intersections":10,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":58,"inner_nodes":49,"leaf_nodes":53,"local_volume_ids":58},"connectivity_records":53,"daughters":51,"local_surface_ids":191,"local_volume_ids":348,"logic_ints":585,"real_ids":53,"reals":272,"rect_arrays":0,"simple_units":4,"surface_types":53,"transforms":51,"universe_indices":4,"universe_types":4,"volume_records":58}})json", + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":9,"max_intersections":10,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":58,"inner_nodes":49,"leaf_nodes":53,"local_volume_ids":58},"connectivity_records":53,"daughters":51,"local_surface_ids":191,"local_volume_ids":348,"logic_ints":585,"real_ids":53,"reals":272,"rect_arrays":0,"simple_units":4,"surface_types":53,"transforms":51,"universe_indices":4,"universe_types":4,"volume_records":58}})json", to_string(out)); } } @@ -1275,7 +1275,7 @@ TEST_F(InputBuilderTest, globalspheres) { OrangeParamsOutput out(this->geometry()); EXPECT_JSON_EQ( - R"json({"scalars":{"max_depth":1,"max_faces":2,"max_intersections":4,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":3,"inner_nodes":0,"leaf_nodes":1,"local_volume_ids":3},"connectivity_records":2,"daughters":0,"local_surface_ids":4,"local_volume_ids":4,"logic_ints":7,"real_ids":2,"reals":2,"rect_arrays":0,"simple_units":1,"surface_types":2,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":3}})json", + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":1,"max_faces":2,"max_intersections":4,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":3,"inner_nodes":0,"leaf_nodes":1,"local_volume_ids":3},"connectivity_records":2,"daughters":0,"local_surface_ids":4,"local_volume_ids":4,"logic_ints":7,"real_ids":2,"reals":2,"rect_arrays":0,"simple_units":1,"surface_types":2,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":3}})json", to_string(out)); } } @@ -1308,7 +1308,7 @@ TEST_F(InputBuilderTest, bgspheres) { OrangeParamsOutput out(this->geometry()); EXPECT_JSON_EQ( - R"json({"scalars":{"max_depth":1,"max_faces":3,"max_intersections":6,"max_logic_depth":1,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":4,"inner_nodes":1,"leaf_nodes":2,"local_volume_ids":4},"connectivity_records":3,"daughters":0,"local_surface_ids":6,"local_volume_ids":3,"logic_ints":5,"real_ids":3,"reals":9,"rect_arrays":0,"simple_units":1,"surface_types":3,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":4}})json", + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":1,"max_faces":3,"max_intersections":6,"max_logic_depth":1,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":4,"inner_nodes":1,"leaf_nodes":2,"local_volume_ids":4},"connectivity_records":3,"daughters":0,"local_surface_ids":6,"local_volume_ids":3,"logic_ints":5,"real_ids":3,"reals":9,"rect_arrays":0,"simple_units":1,"surface_types":3,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":4}})json", to_string(out)); } } @@ -1418,7 +1418,7 @@ TEST_F(InputBuilderTest, hierarchy) { OrangeParamsOutput out(this->geometry()); EXPECT_JSON_EQ( - R"json({"scalars":{"max_depth":3,"max_faces":8,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":24,"inner_nodes":9,"leaf_nodes":16,"local_volume_ids":24},"connectivity_records":13,"daughters":6,"local_surface_ids":20,"local_volume_ids":18,"logic_ints":31,"real_ids":13,"reals":46,"rect_arrays":0,"simple_units":7,"surface_types":13,"transforms":6,"universe_indices":7,"universe_types":7,"volume_records":24}})json", + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":8,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":24,"inner_nodes":9,"leaf_nodes":16,"local_volume_ids":24},"connectivity_records":13,"daughters":6,"local_surface_ids":20,"local_volume_ids":18,"logic_ints":31,"real_ids":13,"reals":46,"rect_arrays":0,"simple_units":7,"surface_types":13,"transforms":6,"universe_indices":7,"universe_types":7,"volume_records":24}})json", to_string(out)); } } From be24f924c231a8d53ddafe849a1d8be71cc32ba7 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Thu, 25 Apr 2024 14:36:24 -0400 Subject: [PATCH 22/59] Add optical track data classes (#1163) --- app/celer-sim/Runner.cc | 2 + src/celeritas/CMakeLists.txt | 1 + src/celeritas/global/CoreTrackData.hh | 5 +- src/celeritas/optical/OpticalCollector.cc | 2 + src/celeritas/optical/OpticalCollector.hh | 15 +- src/celeritas/optical/OpticalTrackData.cc | 60 ++++++++ src/celeritas/optical/OpticalTrackData.hh | 162 ++++++++++++++++++++++ 7 files changed, 240 insertions(+), 7 deletions(-) create mode 100644 src/celeritas/optical/OpticalTrackData.cc create mode 100644 src/celeritas/optical/OpticalTrackData.hh diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 2a15a2453f..34fab17d77 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -405,6 +405,8 @@ void Runner::build_core_params(RunnerInput const& inp, }(); core_params_ = std::make_shared(std::move(params)); + + // TODO: if optical is enabled, construct from imported and core_params_ } //---------------------------------------------------------------------------// diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 15c380e6d1..38ac75a26c 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -68,6 +68,7 @@ list(APPEND SOURCES optical/ScintillationParams.cc optical/OpticalCollector.cc optical/OpticalPropertyParams.cc + optical/OpticalTrackData.cc phys/CutoffParams.cc phys/ImportedModelAdapter.cc phys/ImportedProcessAdapter.cc diff --git a/src/celeritas/global/CoreTrackData.hh b/src/celeritas/global/CoreTrackData.hh index 0337548697..0fa21a26a2 100644 --- a/src/celeritas/global/CoreTrackData.hh +++ b/src/celeritas/global/CoreTrackData.hh @@ -72,7 +72,7 @@ struct CoreParamsData explicit CELER_FUNCTION operator bool() const { return geometry && geo_mats && materials && particles && cutoffs - && physics && sim && init && scalars; + && physics && rng && sim && init && scalars; } //! Assign from another set of data @@ -103,9 +103,6 @@ struct CoreParamsData template struct CoreStateData { - template - using Items = StateCollection; - template using ThreadItems = Collection; diff --git a/src/celeritas/optical/OpticalCollector.cc b/src/celeritas/optical/OpticalCollector.cc index 75fb2de18c..0b68e3b9d1 100644 --- a/src/celeritas/optical/OpticalCollector.cc +++ b/src/celeritas/optical/OpticalCollector.cc @@ -47,6 +47,8 @@ OpticalCollector::OpticalCollector(Input inp) inp.scintillation, storage_); inp.action_registry->insert(pregen_action_); + + // TODO: add an action to launch optical tracking loop } //---------------------------------------------------------------------------// diff --git a/src/celeritas/optical/OpticalCollector.hh b/src/celeritas/optical/OpticalCollector.hh index a3974b5b1f..dde9b20eda 100644 --- a/src/celeritas/optical/OpticalCollector.hh +++ b/src/celeritas/optical/OpticalCollector.hh @@ -31,9 +31,15 @@ struct OpticalGenStorage; /*! * Generate scintillation and Cerenkov optical distribution data at each step. * - * This builds the actions for gathering the pre-step data needed to generate - * the optical distributions and generating the optical distributions at the - * end of the step. + * This class is the interface between the main stepping loop and the photon + * stepping loop and constructs kernel actions for: + * - gathering the pre-step data needed to generate the optical distributions + * - generating the optical distributions at the end of the step + * - launching the photon stepping loop + * + * The "collector" (TODO: rename?) will "own" the optical state data and + * optical params since it's the only thing that launches the optical stepping + * loop. */ class OpticalCollector { @@ -51,6 +57,7 @@ class OpticalCollector SPConstProperties properties; SPConstCerenkov cerenkov; SPConstScintillation scintillation; + // TODO: main core params? ActionRegistry* action_registry; size_type buffer_capacity{}; size_type num_streams{}; @@ -85,6 +92,8 @@ class OpticalCollector SPGenStorage storage_; SPGatherAction gather_action_; SPPreGenAction pregen_action_; + // TODO: tracking loop launcher + // TODO: store optical core params and state? }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/optical/OpticalTrackData.cc b/src/celeritas/optical/OpticalTrackData.cc new file mode 100644 index 0000000000..b09f12f27e --- /dev/null +++ b/src/celeritas/optical/OpticalTrackData.cc @@ -0,0 +1,60 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/OpticalTrackData.cc +//---------------------------------------------------------------------------// +#include "OpticalTrackData.hh" + +#include "corecel/data/CollectionBuilder.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Resize states in host code. + */ +template +void resize(OpticalStateData* state, + HostCRef const& params, + StreamId stream_id, + size_type size) +{ + CELER_EXPECT(params); + CELER_EXPECT(stream_id); + CELER_EXPECT(size > 0); + CELER_VALIDATE(stream_id < params.scalars.max_streams, + << "multitasking stream_id=" << stream_id.unchecked_get() + << " exceeds max_streams=" << params.scalars.max_streams); +#if CELERITAS_CORE_GEO != CELERITAS_CORE_GEO_GEANT4 + resize(&state->geometry, params.geometry, size); +#else + // Geant4 state is stream-local + resize(&state->geometry, params.geometry, stream_id, size); +#endif + resize(&state->materials, size); + resize(&state->physics, params.physics, size); + resize(&state->rng, params.rng, stream_id, size); + resize(&state->sim, size); + resize(&state->init, params.init, size); + state->stream_id = stream_id; + + CELER_ENSURE(state); +} + +//---------------------------------------------------------------------------// +template void +resize(OpticalStateData*, + HostCRef const&, + StreamId, + size_type); + +template void +resize(OpticalStateData*, + HostCRef const&, + StreamId, + size_type); + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/optical/OpticalTrackData.hh b/src/celeritas/optical/OpticalTrackData.hh new file mode 100644 index 0000000000..deac4e81a6 --- /dev/null +++ b/src/celeritas/optical/OpticalTrackData.hh @@ -0,0 +1,162 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/OpticalTrackData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/Types.hh" +#include "celeritas/geo/GeoData.hh" +#include "celeritas/random/RngData.hh" +#include "celeritas/track/SimData.hh" +#include "celeritas/track/TrackInitData.hh" + +#include "Types.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +// XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX +// IMPLEMENT ME! + +template +struct OpticalPhysicsParamsData +{ + explicit CELER_FUNCTION operator bool() const { return false; } +}; +template +struct OpticalPhysicsStateData +{ +}; +template +void resize(OpticalPhysicsStateData*, + HostCRef const&, + size_type) +{ + CELER_NOT_IMPLEMENTED("optical physics state"); +} + +// XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX +//---------------------------------------------------------------------------// +/*! + * Memspace-independent core variables. + */ +struct OpticalScalars +{ + ActionId boundary_action; + + StreamId::size_type max_streams{0}; + OpticalMaterialId::size_type num_materials{0}; + + //! True if assigned and valid + explicit CELER_FUNCTION operator bool() const + { + return boundary_action && max_streams > 0 && num_materials > 0; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Immutable problem data. + */ +template +struct OpticalParamsData +{ + template + using VolumeItems = celeritas::Collection; + + GeoParamsData geometry; + VolumeItems materials; + OpticalPhysicsParamsData physics; + RngParamsData rng; + TrackInitParamsData init; // TODO: don't need max events + + OpticalScalars scalars; + + //! True if all params are assigned + explicit CELER_FUNCTION operator bool() const + { + return geometry && !materials.empty() && physics && rng && init + && scalars; + } + + //! Assign from another set of data + template + OpticalParamsData& operator=(OpticalParamsData const& other) + { + CELER_EXPECT(other); + geometry = other.geometry; + materials = other.materials; + physics = other.physics; + rng = other.rng; + init = other.init; + scalars = other.scalars; + return *this; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Thread-local state data. + */ +template +struct OpticalStateData +{ + template + using Items = StateCollection; + + GeoStateData geometry; + Items materials; + OpticalPhysicsStateData physics; + RngStateData rng; + SimStateData sim; // TODO: has a few things we don't need + TrackInitStateData init; // Still need to track vacancies + + //! Unique identifier for "thread-local" data. + StreamId stream_id; + + //! Number of state elements + CELER_FUNCTION size_type size() const { return geometry.size(); } + + //! Whether the data are assigned + explicit CELER_FUNCTION operator bool() const + { + return geometry && materials && physics && rng && sim && init + && stream_id; + } + + //! Assign from another set of data + template + OpticalStateData& operator=(OpticalStateData& other) + { + CELER_EXPECT(other); + geometry = other.geometry; + materials = other.materials; + physics = other.physics; + rng = other.rng; + sim = other.sim; + init = other.init; + stream_id = other.stream_id; + return *this; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Resize states in host code. + * + * Initialize threads to track slots mapping. + * Resize core states using parameter data, stream ID, and track slots. + */ +template +void resize(OpticalStateData* state, + HostCRef const& params, + StreamId stream_id, + size_type size); + +//---------------------------------------------------------------------------// +} // namespace celeritas From a01607821ed055f6cce2493b4dcbef99de649217 Mon Sep 17 00:00:00 2001 From: Guilherme Lima Date: Thu, 25 Apr 2024 20:35:42 -0500 Subject: [PATCH 23/59] Improve gentrap documentation and fix typos (#1205) * Fix sense of gentrap side faces * Fix unrelated typos * Add test for opposite orientation of input points The points in upper face should still be provided in the same corresponding order as in the lower face. Otherwise a standard trapezoid will have twisted faces, which is still not supported for now. * Improved GenTrap documentation * Revert "Fix sense of gentrap side faces" --- src/corecel/io/Join.hh | 3 +-- src/orange/orangeinp/ConvexRegion.hh | 8 +++---- src/orange/orangeinp/detail/SenseEvaluator.hh | 2 +- test/orange/orangeinp/ConvexRegion.test.cc | 23 +++++++++++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/corecel/io/Join.hh b/src/corecel/io/Join.hh index df1c64f263..f24d6f1bc5 100644 --- a/src/corecel/io/Join.hh +++ b/src/corecel/io/Join.hh @@ -25,8 +25,7 @@ namespace celeritas * The result is a thin class that is streamable. (It can explicitly be * converted to a string with the * \c to_string method). By doing this instead of returning a std::string, - large - * and dynamic containers can be e.g. saved to disk. + * large and dynamic containers can be e.g. saved to disk. */ template detail::Joined diff --git a/src/orange/orangeinp/ConvexRegion.hh b/src/orange/orangeinp/ConvexRegion.hh index a924d73c54..53922f1173 100644 --- a/src/orange/orangeinp/ConvexRegion.hh +++ b/src/orange/orangeinp/ConvexRegion.hh @@ -201,11 +201,9 @@ class Ellipsoid final : public ConvexRegionInterface * A generalized trapezoid, inspired by VecGeom's GenTrap and also ROOT's Arb8. * * A GenTrap represents a general trapezoidal volume with up to eight vertices, - * or two 4-point sitting on two parallel planes perpendicular to Z axis. - * Both sets of up to four points provided need to be in counter-clockwise - * ordering. This ensures that the -z face will have an outward normal, and - * that the +z face points will correspond to their -z face counterparts for - * each side face. + * or two 4-point sets, sitting on two parallel planes perpendicular to Z axis. + * The points in each set might be correspondingly ordered, in such a way to + * properly define the side faces. * TODO: Add a check for this. * TODO: Add proper treatment for degenerate cases. */ diff --git a/src/orange/orangeinp/detail/SenseEvaluator.hh b/src/orange/orangeinp/detail/SenseEvaluator.hh index 0bcf23e934..7f44737bd0 100644 --- a/src/orange/orangeinp/detail/SenseEvaluator.hh +++ b/src/orange/orangeinp/detail/SenseEvaluator.hh @@ -21,7 +21,7 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Evalate whether a point is inside a CSG tree node. + * Evaluate whether a point is inside a CSG tree node. * * This is a construction-time helper that combines \c SenseCalculator with \c * LogicEvaluator. Its intended use is primarily for testing. diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 2bbce11144..8e4667076e 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -510,6 +510,29 @@ TEST_F(GenTrapTest, triang_prism) EXPECT_VEC_SOFT_EQ((Real3{-1, inf, 3}), result.exterior.upper()); } +TEST_F(GenTrapTest, CCWtrap) +{ + auto result + = this->test(GenTrap(40, + {{-19, -30}, {21, -30}, {21, 30}, {-19, 30}}, + {{-21, -30}, {19, -30}, {19, 30}, {-21, 30}})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-40", + "Plane: z=40", + "Plane: y=-30", + "Plane: n={0.99969,-0,0.024992}, d=19.994", + "Plane: y=30", + "Plane: n={0.99969,0,0.024992}, d=-19.994"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -30, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, 30, 40}), result.exterior.upper()); +} + // TODO: this should be valid TEST_F(GenTrapTest, DISABLED_pentahedron) { From be754c741e481b25d438f850335ddef3cc8d89fa Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 26 Apr 2024 09:02:53 -0400 Subject: [PATCH 24/59] Add generic CPU/GPU raytracer for rasterizing (#1191) * Improve documentation * Add raytrace imager, ORANGE instantiation, and test * Update demo rasterizer * Include traits for geo utils * Define "is enabled" geo traits helper * Add raytrace instantiations for vecgeom/geant4 * Change rasterizer geometry to be compatible with standalone orange * Add default constructors to nav collection * Fall back to org.json when using single precision * Cache remaining distance and avoid querying when outside geometry * Add documentation and fix diagnostic test --- app/data/two-boxes.gdml | 30 --- app/demo-rasterizer/CMakeLists.txt | 56 ++-- app/demo-rasterizer/demo-rasterizer.cc | 73 +++-- app/demo-rasterizer/simple-driver.py | 30 ++- cmake/CeleritasUtils.cmake | 17 +- doc/appendix/development.rst | 8 +- src/celeritas/CMakeLists.txt | 5 +- src/celeritas/ext/GeantImporter.cc | 3 +- src/celeritas/ext/GeantImporter.hh | 2 +- src/celeritas/geo/GeoFwd.hh | 27 +- src/celeritas/geo/GeoParams.hh | 2 +- src/geocel/CMakeLists.txt | 5 + src/geocel/GeoTraits.hh | 28 +- src/geocel/g4/GeantGeoParams.hh | 2 +- src/geocel/g4/GeantGeoTraits.hh | 13 +- src/geocel/g4/RaytraceImager.cc | 25 ++ src/geocel/g4/RaytraceImager.hh | 21 ++ src/geocel/g4/detail/GeantGeoNavCollection.hh | 4 + src/geocel/rasterize/RaytraceImager.cuda.t.hh | 70 +++++ src/geocel/rasterize/RaytraceImager.hh | 92 +++++++ .../rasterize/RaytraceImager.nocuda.t.hh | 41 +++ src/geocel/rasterize/RaytraceImager.t.hh | 167 ++++++++++++ src/geocel/rasterize/Raytracer.hh | 79 ++++-- .../rasterize/detail/RaytraceExecutor.hh | 89 ++++++ src/geocel/vg/RaytraceImager.cc | 24 ++ src/geocel/vg/RaytraceImager.cu | 24 ++ src/geocel/vg/RaytraceImager.hh | 21 ++ src/geocel/vg/VecgeomGeoTraits.hh | 13 +- src/geocel/vg/detail/VecgeomNavCollection.hh | 6 +- src/orange/CMakeLists.txt | 2 + src/orange/OrangeGeoTraits.hh | 4 +- src/orange/OrangeParams.cc | 5 +- src/orange/RaytraceImager.cc | 24 ++ src/orange/RaytraceImager.cu | 24 ++ src/orange/RaytraceImager.hh | 21 ++ test/celeritas/user/Diagnostic.test.cc | 50 ++-- test/geocel/MockGeoTrackView.hh | 37 +-- test/geocel/rasterize/Raytracer.test.cc | 16 +- test/orange/CMakeLists.txt | 1 + test/orange/RaytraceImager.test.cc | 254 ++++++++++++++++++ 40 files changed, 1224 insertions(+), 191 deletions(-) delete mode 100644 app/data/two-boxes.gdml create mode 100644 src/geocel/g4/RaytraceImager.cc create mode 100644 src/geocel/g4/RaytraceImager.hh create mode 100644 src/geocel/rasterize/RaytraceImager.cuda.t.hh create mode 100644 src/geocel/rasterize/RaytraceImager.hh create mode 100644 src/geocel/rasterize/RaytraceImager.nocuda.t.hh create mode 100644 src/geocel/rasterize/RaytraceImager.t.hh create mode 100644 src/geocel/rasterize/detail/RaytraceExecutor.hh create mode 100644 src/geocel/vg/RaytraceImager.cc create mode 100644 src/geocel/vg/RaytraceImager.cu create mode 100644 src/geocel/vg/RaytraceImager.hh create mode 100644 src/orange/RaytraceImager.cc create mode 100644 src/orange/RaytraceImager.cu create mode 100644 src/orange/RaytraceImager.hh create mode 100644 test/orange/RaytraceImager.test.cc diff --git a/app/data/two-boxes.gdml b/app/data/two-boxes.gdml deleted file mode 100644 index 60ef18c748..0000000000 --- a/app/data/two-boxes.gdml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/demo-rasterizer/CMakeLists.txt b/app/demo-rasterizer/CMakeLists.txt index 55d4d13191..25d11720a6 100644 --- a/app/demo-rasterizer/CMakeLists.txt +++ b/app/demo-rasterizer/CMakeLists.txt @@ -7,16 +7,19 @@ set(SOURCES demo-rasterizer.cc ) - set(_rasterizer_libs Celeritas::corecel Celeritas::geocel + Celeritas::orange nlohmann_json::nlohmann_json ) if(CELERITAS_CORE_GEO STREQUAL "ORANGE") list(APPEND _rasterizer_libs Celeritas::orange) elseif(CELERITAS_CORE_GEO STREQUAL "VecGeom") list(APPEND _rasterizer_libs VecGeom::vecgeom) +elseif(CELERITAS_CORE_GEO STREQUAL "Geant4") + celeritas_get_g4libs(_g4_geo_libs geometry) + list(APPEND _rasterizer_libs ${_g4_geo_libs}) endif() add_executable(demo-rasterizer ${SOURCES}) @@ -31,27 +34,42 @@ if(NOT BUILD_TESTING) endif() set(_driver "${CMAKE_CURRENT_SOURCE_DIR}/simple-driver.py") -set(_gdml_inp "${CELER_APP_DATA_DIR}/two-boxes.gdml") -add_test(NAME "app/demo-rasterizer" - COMMAND "$" "${_driver}" "${_gdml_inp}" -) - -if(TRUE) - # demo rasterizer is disabled until the next PR - set(_maybe_disable DISABLED true) -else() - set(_maybe_disable) -endif() +set(_model "${CELER_APP_DATA_DIR}/simple-cms.gdml") set(_env "CELERITAS_DEMO_EXE=$" "CELER_DISABLE_PARALLEL=1" ) -set_tests_properties("app/demo-rasterizer" PROPERTIES - ENVIRONMENT "${_env}" - RESOURCE_LOCK gpu - REQUIRED_FILES "${_driver};${_gdml_inp}" - LABELS "app;nomemcheck;gpu" - ${_maybe_disable} -) + +#-----------------------------------------------------------------------------# +function(celer_app_test ext) + set(_labels app nomemcheck) + set(_extra_props) + set(_extra_env "CELER_TEST_EXT=${ext}") + if(ext STREQUAL "gpu") + list(APPEND _labels "gpu") + set(_extra_props RESOURCE_LOCK gpu) + if(CELER_DISABLE_DEVICE) + list(APPEND _extra_props DISABLED true) + endif() + elseif(ext STREQUAL "cpu") + list(APPEND _extra_env "CELER_DISABLE_DEVICE=1") + endif() + + add_test(NAME "app/demo-rasterizer:${ext}" + COMMAND "${CELER_PYTHON}" "${_driver}" "${_model}" + ) + + set_tests_properties("app/demo-rasterizer:${ext}" PROPERTIES + ENVIRONMENT "${_env};${_extra_env}" + REQUIRED_FILES "${_driver};${_gdml_inp}" + LABELS "${_labels}" + ${_props} ${_extra_props} + ) +endfunction() + +#-----------------------------------------------------------------------------# + +celer_app_test("gpu") +celer_app_test("cpu") #-----------------------------------------------------------------------------# diff --git a/app/demo-rasterizer/demo-rasterizer.cc b/app/demo-rasterizer/demo-rasterizer.cc index dc5a78353a..677ed40d49 100644 --- a/app/demo-rasterizer/demo-rasterizer.cc +++ b/app/demo-rasterizer/demo-rasterizer.cc @@ -12,8 +12,8 @@ #include #include +#include "celeritas_config.h" #include "celeritas_version.h" -#include "corecel/cont/Range.hh" #include "corecel/io/Logger.hh" #include "corecel/sys/Device.hh" #include "corecel/sys/DeviceIO.json.hh" @@ -22,6 +22,12 @@ #include "corecel/sys/MpiCommunicator.hh" #include "corecel/sys/ScopedMpiInit.hh" #include "corecel/sys/Stopwatch.hh" +#include "geocel/GeoParamsInterface.hh" +#include "geocel/rasterize/Image.hh" +#include "geocel/rasterize/ImageIO.json.hh" +#include "geocel/rasterize/RaytraceImager.hh" + +// TODO: replace with factory #include "celeritas/geo/GeoParams.hh" namespace celeritas @@ -30,6 +36,29 @@ namespace app { namespace { +//---------------------------------------------------------------------------// +using SPConstGeometry = std::shared_ptr; +using SPImager = std::shared_ptr; + +std::pair +load_geometry_imager(std::string const& filename) +{ + auto geo = std::make_shared(filename); + auto imager = std::make_shared>(geo); + return {std::move(geo), std::move(imager)}; +} + +template +std::shared_ptr> +make_traced_image(std::shared_ptr img_params, + ImagerInterface& generate_image) +{ + auto image = std::make_shared>(std::move(img_params)); + CELER_LOG(status) << "Tracing image on " << to_cstring(M); + generate_image(image.get()); + return image; +} + //---------------------------------------------------------------------------// /*! * Run, launch, and output. @@ -42,8 +71,7 @@ void run(std::istream& is) if (inp.contains("cuda_heap_size")) { - int heapSize = inp.at("cuda_heap_size").get(); - set_cuda_heap_size(heapSize); + set_cuda_heap_size(inp.at("cuda_heap_size").get()); } if (inp.contains("cuda_stack_size")) { @@ -52,11 +80,27 @@ void run(std::istream& is) // Load geometry Stopwatch get_time; - auto geo_params = std::make_shared( - inp.at("input").get().c_str()); + auto&& [geo_params, generate_image] + = load_geometry_imager(inp.at("input").get().c_str()); timers["load"] = get_time(); - CELER_NOT_IMPLEMENTED("demo-rasterizer"); + // Construct image + auto img_params + = std::make_shared(inp.at("image").get()); + + get_time = {}; + std::shared_ptr image; + if (device()) + { + image + = make_traced_image(img_params, *generate_image); + } + else + { + image = make_traced_image(img_params, *generate_image); + } + CELER_ASSERT(image); + timers["trace"] = get_time(); // Get geometry names std::vector vol_names; @@ -66,19 +110,21 @@ void run(std::istream& is) } // Write image - CELER_LOG(status) << "Transferring image from GPU to disk"; + CELER_LOG(status) << "Transferring image and writing to disk"; get_time = {}; + std::string out_filename = inp.at("output"); - std::vector image_data; + std::vector image_data(img_params->num_pixels()); + image->copy_to_host(make_span(image_data)); std::ofstream(out_filename, std::ios::binary) .write(reinterpret_cast(image_data.data()), - image_data.size() * sizeof(decltype(image_data)::value_type)); + image_data.size() * sizeof(int)); timers["write"] = get_time(); // Construct json output CELER_LOG(status) << "Exporting JSON metadata"; nlohmann::json outp = { - {"metadata", nullptr}, + {"metadata", *img_params}, {"data", out_filename}, {"volumes", vol_names}, {"timers", timers}, @@ -91,6 +137,7 @@ void run(std::istream& is) }, }, }; + outp["metadata"]["int_size"] = sizeof(int); std::cout << outp.dump() << std::endl; CELER_LOG(info) << "Exported image to " << out_filename; } @@ -148,12 +195,6 @@ int main(int argc, char* argv[]) // Initialize GPU activate_device(); - if (!device()) - { - CELER_LOG(critical) << "CUDA capability is disabled"; - return EXIT_FAILURE; - } - try { CELER_ASSERT(instream_ptr); diff --git a/app/demo-rasterizer/simple-driver.py b/app/demo-rasterizer/simple-driver.py index 6768df0060..0ba6f94ff9 100755 --- a/app/demo-rasterizer/simple-driver.py +++ b/app/demo-rasterizer/simple-driver.py @@ -9,29 +9,33 @@ import subprocess from os import environ from sys import exit, argv +from pathlib import Path try: - (gdml_filename,) = argv[1:] + (model_file,) = argv[1:] except TypeError: print("usage: {} inp.gdml".format(sys.argv[0])) exit(2) +exe = environ.get("CELERITAS_DEMO_EXE", "./demo-rasterizer") +ext = environ.get("CELER_TEST_EXT", "unknown") + +problem_name = "-".join([Path(model_file).stem, ext]) + +eps = 0.01 inp = { 'image': { # TODO: input is cm for now; add 'units' argument? - 'lower_left': [-10, -10, 0], - 'upper_right': [10, 10, 0], - 'rightward_ax': [1, 0, 0], - 'vertical_pixels': 32 + 'lower_left': [-800, 0, -1500], + 'upper_right': [800, 0, 1600], + 'rightward': [1, 0, 0], + 'vertical_pixels': 128, }, - 'input': gdml_filename, - 'output': 'two-boxes.bin' + 'input': model_file, + 'output': f"{problem_name}.bin" } - -exe = environ.get('CELERITAS_DEMO_EXE', './demo-rasterizer') - print("Input:") -with open(f'{exe}.inp.json', 'w') as f: +with open(f"{problem_name}.inp.json", 'w') as f: json.dump(inp, f, indent=1) print(json.dumps(inp, indent=1)) @@ -52,6 +56,6 @@ print(out_text) print("fatal:", str(e)) exit(1) -print(json.dumps(result, indent=1)) -with open(f'{exe}.out.json', 'w') as f: +print(json.dumps(result["metadata"], indent=1)) +with open(f'{problem_name}.out.json', 'w') as f: json.dump(result, f) diff --git a/cmake/CeleritasUtils.cmake b/cmake/CeleritasUtils.cmake index e78007268d..82fd4bb7d9 100644 --- a/cmake/CeleritasUtils.cmake +++ b/cmake/CeleritasUtils.cmake @@ -155,7 +155,13 @@ CMake configuration utility functions for Celeritas. The ```` must be a relative path to the current source directory, and the ``` path is configured to the project build "include" directory. -.. comand:: celeritas_error_incompatible_option +.. command:: celeritas_polysource_append + + Add C++ and CUDA/HIP source files based on the enabled options. + + celeritas_polysource_append(SOURCES my/Class) + +.. command:: celeritas_error_incompatible_option Print a descriptive failure message about conflicting cmake options. @@ -493,6 +499,15 @@ endfunction() #-----------------------------------------------------------------------------# +macro(celeritas_polysource_append var filename_we) + list(APPEND ${var} "${filename_we}.cc") + if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP) + list(APPEND ${var} "${filename_we}.cu") + endif() +endmacro() + +#-----------------------------------------------------------------------------# + function(celeritas_error_incompatible_option msg var new_value) message(SEND_ERROR "Invalid setting ${var}=${${var}}: ${msg} Possible fix: cmake -D${var}=${new_value} ${CMAKE_BINARY_DIR}" diff --git a/doc/appendix/development.rst b/doc/appendix/development.rst index 04179c8001..ad666ea8f1 100644 --- a/doc/appendix/development.rst +++ b/doc/appendix/development.rst @@ -254,10 +254,10 @@ Thus we have the following rules: Some files have special extensions: -- ``.t.hh`` is for non-inlined ``template`` implementations that can be - included and instantiated elsewhere. However, if the function - declaration in the ``.hh`` file is declared ``inline``, the definition - should be provided there as well. +- ``.t.hh`` is for non-inlined ``template`` implementations intended to be + included and explicitly instantiated in a downstream C++ or CUDA compilation + unit. Note that if the function in the ``.hh`` file is declared ``inline``, + the definition must be provided in the header as well. - ``.test.cc`` are unit test executables corresponding to the main ``.cc`` file. These should only be in the main ``/test`` directory. diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 38ac75a26c..3ff4bdfffb 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -235,10 +235,7 @@ endif() #-----------------------------------------------------------------------------# macro(celeritas_polysource filename_we) - list(APPEND SOURCES "${filename_we}.cc") - if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP) - list(APPEND SOURCES "${filename_we}.cu") - endif() + celeritas_polysource_append(SOURCES ${filename_we}) endmacro() celeritas_polysource(em/model/BetheHeitlerModel) diff --git a/src/celeritas/ext/GeantImporter.cc b/src/celeritas/ext/GeantImporter.cc index 883598ecb5..d28bf1a4a1 100644 --- a/src/celeritas/ext/GeantImporter.cc +++ b/src/celeritas/ext/GeantImporter.cc @@ -590,8 +590,7 @@ import_materials(GeantImporter::DataSelection::Flags particle_flags) materials.resize(g4production_cuts_table.GetTableSize()); CELER_VALIDATE(!materials.empty(), << "no Geant4 production cuts are defined (you may " - "need " - "to call G4RunManager::RunInitialization)"); + "need to call G4RunManager::RunInitialization)"); using CutRange = std::pair>; diff --git a/src/celeritas/ext/GeantImporter.hh b/src/celeritas/ext/GeantImporter.hh index 2cefc4330c..5c01135b2b 100644 --- a/src/celeritas/ext/GeantImporter.hh +++ b/src/celeritas/ext/GeantImporter.hh @@ -73,7 +73,7 @@ class GeantImporter final : public ImporterInterface //!@} public: - // Get an externally loaded Geant4 top-level geometry element + // Get the top-level geometry element from the run manager+navigator static G4VPhysicalVolume const* get_world_volume(); // Construct from an existing Geant4 geometry, assuming physics is loaded diff --git a/src/celeritas/geo/GeoFwd.hh b/src/celeritas/geo/GeoFwd.hh index fae423d19a..cf981d9e6c 100644 --- a/src/celeritas/geo/GeoFwd.hh +++ b/src/celeritas/geo/GeoFwd.hh @@ -10,33 +10,12 @@ #include "celeritas_config.h" #include "corecel/Types.hh" +#include "geocel/g4/GeantGeoTraits.hh" +#include "geocel/vg/VecgeomGeoTraits.hh" +#include "orange/OrangeGeoTraits.hh" namespace celeritas { -//---------------------------------------------------------------------------// -// FORWARD DECLARATIONS -//---------------------------------------------------------------------------// -class VecgeomParams; -template -struct VecgeomParamsData; -template -struct VecgeomStateData; -class VecgeomTrackView; - -class OrangeParams; -template -struct OrangeStateData; -template -struct OrangeParamsData; -class OrangeTrackView; - -class GeantGeoParams; -template -struct GeantGeoStateData; -template -struct GeantGeoParamsData; -class GeantGeoTrackView; - //---------------------------------------------------------------------------// #if CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_VECGEOM using GeoParams = VecgeomParams; diff --git a/src/celeritas/geo/GeoParams.hh b/src/celeritas/geo/GeoParams.hh index 17dd5641ee..7a11fde8ff 100644 --- a/src/celeritas/geo/GeoParams.hh +++ b/src/celeritas/geo/GeoParams.hh @@ -17,5 +17,5 @@ # include "geocel/g4/GeantGeoParams.hh" // IWYU pragma: export #endif -// Include type alias for GeoParams +// Include traits and type aliases for GeoParams #include "GeoFwd.hh" // IWYU pragma: export diff --git a/src/geocel/CMakeLists.txt b/src/geocel/CMakeLists.txt index ac69342657..c31b8286cf 100644 --- a/src/geocel/CMakeLists.txt +++ b/src/geocel/CMakeLists.txt @@ -51,6 +51,7 @@ if(CELERITAS_USE_Geant4) ScopedGeantExceptionHandler.cc ScopedGeantLogger.cc g4/GeantGeoParams.cc + g4/RaytraceImager.cc g4/detail/GeantGeoNavCollection.cc ) celeritas_get_g4libs(_cg4_libs global geometry persistency intercoms) @@ -68,6 +69,7 @@ if(CELERITAS_USE_VecGeom) vg/VecgeomParams.cc vg/VecgeomParamsOutput.cc vg/detail/VecgeomNavCollection.cc + vg/RaytraceImager.cc ) if(VecGeom_GDML_FOUND) list(APPEND PRIVATE_DEPS VecGeom::vgdml) @@ -83,6 +85,9 @@ if(CELERITAS_USE_VecGeom) # to resolve the symbols generate by the `nvcc -dlink` of # one of the executable. list(APPEND PUBLIC_DEPS VecGeom::vecgeom) + list(APPEND SOURCES + vg/RaytraceImager.cu + ) elseif(VecGeom_CUDA_FOUND) # VecGeom is built with CUDA but Celeritas is not list(APPEND PRIVATE_DEPS VecGeom::vecgeomcuda) diff --git a/src/geocel/GeoTraits.hh b/src/geocel/GeoTraits.hh index 696e4895f2..119d67f94b 100644 --- a/src/geocel/GeoTraits.hh +++ b/src/geocel/GeoTraits.hh @@ -43,10 +43,34 @@ struct GeoTraits using TrackView = void; //! Descriptive name for the geometry - static inline char const* name = nullptr; + static constexpr inline char const* name = nullptr; //! TO BE REMOVED: "native" file extension for this geometry - static inline char const* ext = nullptr; + static constexpr inline char const* ext = nullptr; +}; + +//---------------------------------------------------------------------------// +//! Helper for determining whether a geometry type is available. +template +inline constexpr bool is_geometry_configured_v + = !std::is_void_v::TrackView>; + +//---------------------------------------------------------------------------// +/*! + * Traits class for marking a geometry as not configured. + * + * Specializations should inherit from this class when the geometry is not + * configured. + */ +struct NotConfiguredGeoTraits +{ + template + using ParamsData = void; + template + using StateData = void; + using TrackView = void; + static constexpr inline char const* name = nullptr; + static constexpr inline char const* ext = nullptr; }; //---------------------------------------------------------------------------// diff --git a/src/geocel/g4/GeantGeoParams.hh b/src/geocel/g4/GeantGeoParams.hh index 4ad0352e72..33ac1a0bad 100644 --- a/src/geocel/g4/GeantGeoParams.hh +++ b/src/geocel/g4/GeantGeoParams.hh @@ -30,7 +30,7 @@ class ScopedGeantExceptionHandler; /*! * Shared Geant4 geometry model wrapper. * - * This can be consructed directly by loading a GDML file, or in-memory using + * This can be constructed directly by loading a GDML file, or in-memory using * an existing physical volume. One "gotcha" is that due to persistent static * variables in Geant4, the volume IDs will be offset if a geometry has been * loaded and closed previously. diff --git a/src/geocel/g4/GeantGeoTraits.hh b/src/geocel/g4/GeantGeoTraits.hh index 601da0c107..8e9e1dfcd4 100644 --- a/src/geocel/g4/GeantGeoTraits.hh +++ b/src/geocel/g4/GeantGeoTraits.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "celeritas_config.h" #include "geocel/GeoTraits.hh" namespace celeritas @@ -19,6 +20,7 @@ struct GeantGeoParamsData; template struct GeantGeoStateData; +#if CELERITAS_USE_GEANT4 //---------------------------------------------------------------------------// /*! * Traits specialization for Geant4 geometry. @@ -38,11 +40,18 @@ struct GeoTraits using TrackView = GeantGeoTrackView; //! Descriptive name for the geometry - static inline char const* name = "Geant4"; + static constexpr inline char const* name = "Geant4"; //! TO BE REMOVED: "native" file extension for this geometry - static inline char const* ext = ".gdml"; + static constexpr inline char const* ext = ".gdml"; }; +#else +//! Geant4 is unavailable +template<> +struct GeoTraits : NotConfiguredGeoTraits +{ +}; +#endif //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/geocel/g4/RaytraceImager.cc b/src/geocel/g4/RaytraceImager.cc new file mode 100644 index 0000000000..ec6d348145 --- /dev/null +++ b/src/geocel/g4/RaytraceImager.cc @@ -0,0 +1,25 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/g4/RaytraceImager.cc +//---------------------------------------------------------------------------// +#include "RaytraceImager.hh" + +#include "geocel/rasterize/RaytraceImager.nocuda.t.hh" +#include "geocel/rasterize/RaytraceImager.t.hh" + +#include "GeantGeoData.hh" +#include "GeantGeoParams.hh" +#include "GeantGeoTrackView.hh" +#include "GeantGeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/g4/RaytraceImager.hh b/src/geocel/g4/RaytraceImager.hh new file mode 100644 index 0000000000..49362e1689 --- /dev/null +++ b/src/geocel/g4/RaytraceImager.hh @@ -0,0 +1,21 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/g4/RaytraceImager.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/rasterize/RaytraceImager.hh" + +#include "GeantGeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +extern template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/g4/detail/GeantGeoNavCollection.hh b/src/geocel/g4/detail/GeantGeoNavCollection.hh index 2da571bc5c..2dd121da40 100644 --- a/src/geocel/g4/detail/GeantGeoNavCollection.hh +++ b/src/geocel/g4/detail/GeantGeoNavCollection.hh @@ -96,6 +96,10 @@ struct GeantGeoNavCollection Span touch_handles; Span navigators; + // Default constructors + GeantGeoNavCollection() = default; + GeantGeoNavCollection(GeantGeoNavCollection const&) = default; + // Obtain reference from host memory GeantGeoNavCollection& operator=(GeantGeoNavCollection& other); diff --git a/src/geocel/rasterize/RaytraceImager.cuda.t.hh b/src/geocel/rasterize/RaytraceImager.cuda.t.hh new file mode 100644 index 0000000000..40f9299c3a --- /dev/null +++ b/src/geocel/rasterize/RaytraceImager.cuda.t.hh @@ -0,0 +1,70 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +/*! + * \file geocel/rasterize/RaytraceImager.cuda.t.hh + * \brief Template definition file for \c RaytraceImager . + * + * Include this file in a .cu file and instantiate it explicitly. When + * instantiating, you must provide access to the GeoTraits specialization as + * well as the data classes and track view. + */ +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/rasterize/RaytraceImager.hh" + +#include "celeritas_config.h" +#include "corecel/Macros.hh" +#include "corecel/sys/KernelParamCalculator.device.hh" +#include "corecel/sys/ThreadId.hh" + +#include "detail/RaytraceExecutor.hh" + +namespace celeritas +{ +namespace +{ +//---------------------------------------------------------------------------// +template +__global__ void __launch_bounds__(CELERITAS_MAX_BLOCK_SIZE) + raytrace_kernel(ThreadId::size_type num_threads, F execute_thread) +{ + auto tid = celeritas::KernelParamCalculator::thread_id(); + if (!(tid < num_threads)) + return; + execute_thread(tid); +} + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +/*! + * Launch the raytrace kernel on device. + */ +template +void RaytraceImager::launch_raytrace_kernel( + GeoParamsCRef const& geo_params, + GeoStateRef const& geo_states, + ImageParamsCRef const& img_params, + ImageStateRef const& img_states) const +{ + using CalcId = detail::VolumeIdCalculator; + using Executor = detail::RaytraceExecutor; + + static KernelParamCalculator const calc_launch_params{ + "raytrace", &raytrace_kernel}; + auto config = calc_launch_params(geo_states.size()); + raytrace_kernel + <<>>( + geo_states.size(), + Executor{geo_params, geo_states, img_params, img_states, CalcId{}}); + + CELER_DEVICE_CALL_PREFIX(DeviceSynchronize()); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/RaytraceImager.hh b/src/geocel/rasterize/RaytraceImager.hh new file mode 100644 index 0000000000..61df6700e0 --- /dev/null +++ b/src/geocel/rasterize/RaytraceImager.hh @@ -0,0 +1,92 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/RaytraceImager.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "geocel/GeoTraits.hh" + +#include "ImageInterface.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +template +struct ImageParamsData; +template +struct ImageStateData; + +//---------------------------------------------------------------------------// +/*! + * Generate one or more images from a geometry by raytracing. + */ +template +class RaytraceImager final : public ImagerInterface +{ + public: + //!@{ + //! \name Type aliases + using SPGeometry = std::shared_ptr; + //!@} + + public: + // Construct with geometry + explicit RaytraceImager(SPGeometry geo); + + // Raytrace an image on host or device + void operator()(Image* image) final; + void operator()(Image* image) final; + + private: + //// TYPES //// + + using GTraits = GeoTraits; + template + using GeoStateData = typename GTraits::template StateData; + template + using GeoParamsData = typename GTraits::template ParamsData; + using GeoTrackView = typename GTraits::TrackView; + + template + using GeoParamsCRef = GeoParamsData; + template + using GeoStateRef = GeoStateData; + template + using ImageParamsCRef = ImageParamsData; + template + using ImageStateRef = ImageStateData; + + struct CachedStates; + + //// DATA //// + + SPGeometry geo_; + std::shared_ptr cache_; + + //// MEMBER FUNCTIONS //// + + CELER_DEFAULT_MOVE_DELETE_COPY(RaytraceImager); + + template + void raytrace_impl(Image* image); + + void + launch_raytrace_kernel(GeoParamsCRef const& geo_params, + GeoStateRef const& geo_states, + ImageParamsCRef const& img_params, + ImageStateRef const& img_state) const; + + void launch_raytrace_kernel( + GeoParamsCRef const& geo_params, + GeoStateRef const& geo_states, + ImageParamsCRef const& img_params, + ImageStateRef const& img_state) const; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/RaytraceImager.nocuda.t.hh b/src/geocel/rasterize/RaytraceImager.nocuda.t.hh new file mode 100644 index 0000000000..21b7a73ed2 --- /dev/null +++ b/src/geocel/rasterize/RaytraceImager.nocuda.t.hh @@ -0,0 +1,41 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +/*! + * \file geocel/rasterize/RaytraceImager.nocuda.t.hh + * \brief Template definitions for \c RaytraceImager when CUDA is unsupported + * + * If a particular geometry does not support device raytracing, include this + * file alongside \c RaytraceImager.t.hh before instantiating \c + * RaytraceImager. + */ +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/rasterize/RaytraceImager.hh" + +#include "celeritas_config.h" +#include "corecel/Macros.hh" + +namespace celeritas +{ +#if CELER_USE_DEVICE +//---------------------------------------------------------------------------// +/*! + * Launch the raytrace kernel on device. + */ +template +void RaytraceImager::launch_raytrace_kernel( + GeoParamsCRef const&, + GeoStateRef const&, + ImageParamsCRef const&, + ImageStateRef const&) const +{ + CELER_ASSERT_UNREACHABLE(); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/RaytraceImager.t.hh b/src/geocel/rasterize/RaytraceImager.t.hh new file mode 100644 index 0000000000..8e0272d571 --- /dev/null +++ b/src/geocel/rasterize/RaytraceImager.t.hh @@ -0,0 +1,167 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +/*! + * \file geocel/rasterize/RaytraceImager.t.hh + * \brief Template definition file for \c RaytraceImager + * + * Include this file in a .cc file and instantiate it explicitly. When + * instantiating, you must provide access to the GeoTraits specialization as + * well as the data classes and track view. + */ +//---------------------------------------------------------------------------// +#pragma once + +#include "RaytraceImager.hh" + +#include "corecel/data/CollectionStateStore.hh" +#include "corecel/sys/MultiExceptionHandler.hh" + +#include "Image.hh" + +#include "detail/RaytraceExecutor.hh" + +#define CELER_INST_RAYTRACE_IMAGER + +namespace celeritas +{ +//---------------------------------------------------------------------------// +template +struct RaytraceImager::CachedStates +{ + template + using StateStore = CollectionStateStore; + + StateStore host; + StateStore device; + + //! Access the states for the given memspace + template + StateStore& get() + { + if constexpr (M == MemSpace::host) + { + return host; + } + else + { + return device; + } + } +}; + +//---------------------------------------------------------------------------// +/*! + * Construct with geometry. + */ +template +RaytraceImager::RaytraceImager(SPGeometry geo) + : geo_{std::move(geo)}, cache_{std::make_shared()} +{ + CELER_EXPECT(geo_); + CELER_ENSURE(cache_); +} + +//---------------------------------------------------------------------------// +/*! + * Raytrace an image on host or device. + */ +template +void RaytraceImager::operator()(Image* image) +{ + return this->raytrace_impl(image); +} + +//---------------------------------------------------------------------------// +/*! + * Raytrace an image on host or device. + */ +template +void RaytraceImager::operator()(Image* image) +{ + return this->raytrace_impl(image); +} + +//---------------------------------------------------------------------------// +/*! + * Raytrace an image on host or device. + */ +template +template +void RaytraceImager::raytrace_impl(Image* image) +{ + CELER_EXPECT(image); + + auto const& img_params = *image->params(); + auto const& geo_params = *geo_; + auto& geo_state_store = cache_->template get(); + + if (img_params.num_lines() != geo_state_store.size()) + { + using StateStore = typename CachedStates::template StateStore; + + // Allocate (or deallocate and reallocate) geometry states + if (geo_state_store) + { + geo_state_store = {}; + } + geo_state_store + = StateStore{geo_params.host_ref(), img_params.num_lines()}; + } + + // Raytrace it! + this->launch_raytrace_kernel(geo_params.template ref(), + geo_state_store.ref(), + img_params.template ref(), + image->ref()); +} + +//---------------------------------------------------------------------------// +/*! + * Execute the raytrace on the host. + */ +template +void RaytraceImager::launch_raytrace_kernel( + GeoParamsCRef const& geo_params, + GeoStateRef const& geo_states, + ImageParamsCRef const& img_params, + ImageStateRef const& img_state) const +{ + using CalcId = detail::VolumeIdCalculator; + using Executor = detail::RaytraceExecutor; + Executor execute_thread{ + geo_params, geo_states, img_params, img_state, CalcId{}}; + + size_type const num_threads = geo_states.size(); + + MultiExceptionHandler capture_exception; +#ifdef _OPENMP +# pragma omp parallel for +#endif + for (size_type i = 0; i < num_threads; ++i) + { + CELER_TRY_HANDLE(execute_thread(ThreadId{i}), capture_exception); + } + log_and_rethrow(std::move(capture_exception)); +} + +//---------------------------------------------------------------------------// +/*! + * Execute the raytrace on the device. + */ +#if !CELER_USE_DEVICE +template +void RaytraceImager::launch_raytrace_kernel( + GeoParamsCRef const&, + GeoStateRef const&, + ImageParamsCRef const&, + ImageStateRef const&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/rasterize/Raytracer.hh b/src/geocel/rasterize/Raytracer.hh index f4bbe42fa4..258ba6b6da 100644 --- a/src/geocel/rasterize/Raytracer.hh +++ b/src/geocel/rasterize/Raytracer.hh @@ -44,13 +44,18 @@ class Raytracer F calc_id_; ImageLineView const& image_; - size_type pixel_; + size_type pixel_{invalid_pixel()}; //!< Current pixel + real_type distance_{0}; //!< Distance to next boundary + int cur_id_{-1}; //!< Current ID //// HELPER FUNCTIONS //// // Initialize the geometry at this pixel index inline CELER_FUNCTION void initialize_at_pixel(size_type pix); + // Find the next step + inline CELER_FUNCTION void find_next_step(); + //! Sentinel value for needing the pixel to be reset static CELER_CONSTEXPR_FUNCTION size_type invalid_pixel() { @@ -84,7 +89,6 @@ Raytracer::Raytracer(GTV&& geo, F&& calc_id, ImageLineView const& image) : geo_{celeritas::forward(geo)} , calc_id_{celeritas::forward(calc_id)} , image_{image} - , pixel_{invalid_pixel()} { } @@ -98,35 +102,41 @@ CELER_FUNCTION auto Raytracer::operator()(size_type pix) -> result_type if (pix != pixel_) { this->initialize_at_pixel(pix); + if (pixel_ == invalid_pixel()) + { + // Pixel starts outside the geometry + return cur_id_; + } } + // Remaining distance to the end of the pixel real_type pix_dist = image_.pixel_width(); + // Distance associated with the largest segment so far real_type max_dist = 0; - int cur_id = this->calc_id_(geo_); - int max_id = cur_id; + int max_id = cur_id_; + // Countdown to limit stuck tracks or overdetailed pixels auto abort_counter = this->max_crossings_per_pixel(); - auto next = geo_.find_next_step(pix_dist); - while (next.boundary && pix_dist > 0) + while (cur_id_ >= 0 && distance_ < pix_dist) { - CELER_ASSERT(next.distance <= pix_dist); // Move to geometry boundary - pix_dist -= next.distance; - - if (max_id == cur_id) + if (max_id == cur_id_) { - max_dist += next.distance; + max_dist += distance_; } - else if (next.distance > max_dist) + else if (distance_ > max_dist) { - max_dist = next.distance; - max_id = cur_id; + max_dist = distance_; + max_id = cur_id_; } + // Update pixel and boundary distance + pix_dist -= distance_; + distance_ = 0; + // Cross surface and update post-crossing ID geo_.move_to_boundary(); geo_.cross_boundary(); - cur_id = this->calc_id_(geo_); if (--abort_counter == 0) { @@ -135,19 +145,19 @@ CELER_FUNCTION auto Raytracer::operator()(size_type pix) -> result_type } if (pix_dist > 0) { - // Next movement is to end of geo_ or pixel - next = geo_.find_next_step(pix_dist); + // Update next distance and current ID for the remaining pixel + this->find_next_step(); } } if (pix_dist > 0) { // Move to pixel boundary - geo_.move_internal(pix_dist); + distance_ -= pix_dist; if (pix_dist > max_dist) { max_dist = pix_dist; - max_id = cur_id; + max_id = cur_id_; } } @@ -176,6 +186,37 @@ CELER_FUNCTION void Raytracer::initialize_at_pixel(size_type pix) axpy(pix * image_.pixel_width(), init.dir, &init.pos); geo_ = init; pixel_ = pix; + distance_ = 0; + this->find_next_step(); +} + +//---------------------------------------------------------------------------// +/*! + * Find the distance to the next step. + */ +template +CELER_FUNCTION void Raytracer::find_next_step() +{ + CELER_EXPECT(pixel_ != invalid_pixel()); + CELER_EXPECT(distance_ <= 0); + if (geo_.is_outside()) + { + // Skip this pixel since not all navigation engines can trace from + // outside the geometry + pixel_ = invalid_pixel(); + distance_ = numeric_limits::infinity(); + } + else + { + // Search to distances just past the end of the saved pixels (so the + // last point isn't coincident with a boundary) + distance_ = geo_.find_next_step((image_.max_index() + 1 - pixel_) + * image_.pixel_width()) + .distance; + } + cur_id_ = this->calc_id_(geo_); + + CELER_ENSURE(distance_ > 0); } //---------------------------------------------------------------------------// diff --git a/src/geocel/rasterize/detail/RaytraceExecutor.hh b/src/geocel/rasterize/detail/RaytraceExecutor.hh new file mode 100644 index 0000000000..d26ff62d9a --- /dev/null +++ b/src/geocel/rasterize/detail/RaytraceExecutor.hh @@ -0,0 +1,89 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/rasterize/detail/RaytraceExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "../ImageData.hh" +#include "../ImageLineView.hh" +#include "../Raytracer.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Get the volume ID as an integer, or -1 if outside. + */ +struct VolumeIdCalculator +{ + template + CELER_FUNCTION int operator()(GTV const& geo) const + { + if (geo.is_outside()) + return -1; + return static_cast(geo.volume_id().get()); + } +}; + +//---------------------------------------------------------------------------// +/*! + * Raytrace a geometry onto an image. + */ +template +struct RaytraceExecutor +{ + //// TYPES //// + + // TODO: Use observer pointers? + using GeoTrackView = GTV; + using GeoParamsRef = typename GTV::ParamsRef; + using GeoStateRef = typename GTV::StateRef; + using ImgParamsRef = NativeCRef; + using ImgStateRef = NativeRef; + + //// DATA //// + + GeoParamsRef geo_params; + GeoStateRef geo_state; + ImgParamsRef img_params; + ImgStateRef img_state; + + F calc_id; + + //// FUNCTIONS //// + + // Initialize track states + inline CELER_FUNCTION void operator()(ThreadId tid) const; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Trace a single line. + */ +template +CELER_FUNCTION void RaytraceExecutor::operator()(ThreadId tid) const +{ + CELER_EXPECT(tid < img_params.scalars.dims[0]); + CELER_EXPECT(geo_state.size() == img_params.scalars.dims[0]); + + // Trace one state per vertical line + GeoTrackView geo{geo_params, geo_state, TrackSlotId{tid.unchecked_get()}}; + ImageLineView line{img_params, img_state, tid.unchecked_get()}; + Raytracer trace(geo, calc_id, line); + for (auto col : range(line.max_index())) + { + int val = trace(col); + line.set_pixel(col, val); + } +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/geocel/vg/RaytraceImager.cc b/src/geocel/vg/RaytraceImager.cc new file mode 100644 index 0000000000..ad1b168f00 --- /dev/null +++ b/src/geocel/vg/RaytraceImager.cc @@ -0,0 +1,24 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/vg/RaytraceImager.cc +//---------------------------------------------------------------------------// +#include "RaytraceImager.hh" + +#include "geocel/rasterize/RaytraceImager.t.hh" + +#include "VecgeomData.hh" +#include "VecgeomGeoTraits.hh" +#include "VecgeomParams.hh" +#include "VecgeomTrackView.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/vg/RaytraceImager.cu b/src/geocel/vg/RaytraceImager.cu new file mode 100644 index 0000000000..037f695e28 --- /dev/null +++ b/src/geocel/vg/RaytraceImager.cu @@ -0,0 +1,24 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/vg/RaytraceImager.cu +//---------------------------------------------------------------------------// +#include "RaytraceImager.hh" + +#include "geocel/rasterize/RaytraceImager.cuda.t.hh" + +#include "VecgeomData.hh" +#include "VecgeomGeoTraits.hh" +#include "VecgeomParams.hh" +#include "VecgeomTrackView.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/vg/RaytraceImager.hh b/src/geocel/vg/RaytraceImager.hh new file mode 100644 index 0000000000..b817c1bf50 --- /dev/null +++ b/src/geocel/vg/RaytraceImager.hh @@ -0,0 +1,21 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file geocel/vg/RaytraceImager.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/rasterize/RaytraceImager.hh" + +#include "VecgeomGeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +extern template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/geocel/vg/VecgeomGeoTraits.hh b/src/geocel/vg/VecgeomGeoTraits.hh index 46c933ff84..bb2ef84683 100644 --- a/src/geocel/vg/VecgeomGeoTraits.hh +++ b/src/geocel/vg/VecgeomGeoTraits.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "celeritas_config.h" #include "geocel/GeoTraits.hh" namespace celeritas @@ -19,6 +20,7 @@ struct VecgeomParamsData; template struct VecgeomStateData; +#if CELERITAS_USE_VECGEOM //---------------------------------------------------------------------------// /*! * Traits specialization for VecGeom geometry. @@ -38,11 +40,18 @@ struct GeoTraits using TrackView = VecgeomTrackView; //! Descriptive name for the geometry - static inline char const* name = "VecGeom"; + static constexpr inline char const* name = "VecGeom"; //! TO BE REMOVED: "native" file extension for this geometry - static inline char const* ext = ".gdml"; + static constexpr inline char const* ext = ".gdml"; }; +#else +//! VecGeom is unavailable +template<> +struct GeoTraits : NotConfiguredGeoTraits +{ +}; +#endif //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/geocel/vg/detail/VecgeomNavCollection.hh b/src/geocel/vg/detail/VecgeomNavCollection.hh index 754206571a..4eb3ca5303 100644 --- a/src/geocel/vg/detail/VecgeomNavCollection.hh +++ b/src/geocel/vg/detail/VecgeomNavCollection.hh @@ -70,10 +70,14 @@ struct VecgeomNavCollection Span nav_state; + // Default construction and copy construction + VecgeomNavCollection() = default; + VecgeomNavCollection(VecgeomNavCollection const&) = default; + // Obtain reference from host memory VecgeomNavCollection& operator=(VecgeomNavCollection& other); - // Default assignment + // Default copy assignment VecgeomNavCollection& operator=(VecgeomNavCollection const&) = default; // Get the navigation state for a given track slot diff --git a/src/orange/CMakeLists.txt b/src/orange/CMakeLists.txt index 82de4e8304..a0ae520518 100644 --- a/src/orange/CMakeLists.txt +++ b/src/orange/CMakeLists.txt @@ -100,6 +100,8 @@ if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") list(APPEND PRIVATE_DEPS geocel_g4org) endif() +celeritas_polysource_append(SOURCES RaytraceImager) + #-----------------------------------------------------------------------------# # Create library #-----------------------------------------------------------------------------# diff --git a/src/orange/OrangeGeoTraits.hh b/src/orange/OrangeGeoTraits.hh index 9b9cddcf4c..7979870d6a 100644 --- a/src/orange/OrangeGeoTraits.hh +++ b/src/orange/OrangeGeoTraits.hh @@ -38,10 +38,10 @@ struct GeoTraits using TrackView = OrangeTrackView; //! Descriptive name for the geometry - static inline char const* name = "ORANGE"; + static constexpr inline char const* name = "ORANGE"; //! TO BE REMOVED: "native" file extension for this geometry - static inline char const* ext = ".org.json"; + static constexpr inline char const* ext = ".org.json"; }; //---------------------------------------------------------------------------// diff --git a/src/orange/OrangeParams.cc b/src/orange/OrangeParams.cc index 306ed71581..594e9a272c 100644 --- a/src/orange/OrangeParams.cc +++ b/src/orange/OrangeParams.cc @@ -81,7 +81,8 @@ OrangeInput input_from_file(std::string filename) { if (ends_with(filename, ".gdml")) { - if (CELERITAS_USE_GEANT4) + if (CELERITAS_USE_GEANT4 + && CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) { // Load with Geant4: must *not* be using run manager auto* world = ::celeritas::load_geant_geometry_native(filename); @@ -92,7 +93,7 @@ OrangeInput input_from_file(std::string filename) else { CELER_LOG(warning) << "Using ORANGE geometry with GDML suffix " - "when Geant4 is disabled: trying " + "when Geant4 conversion is disabled: trying " "`.org.json` instead"; filename.erase(filename.end() - 5, filename.end()); filename += ".org.json"; diff --git a/src/orange/RaytraceImager.cc b/src/orange/RaytraceImager.cc new file mode 100644 index 0000000000..824621f244 --- /dev/null +++ b/src/orange/RaytraceImager.cc @@ -0,0 +1,24 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/RaytraceImager.cc +//---------------------------------------------------------------------------// +#include "RaytraceImager.hh" + +#include "geocel/rasterize/RaytraceImager.t.hh" + +#include "OrangeData.hh" +#include "OrangeGeoTraits.hh" +#include "OrangeParams.hh" +#include "OrangeTrackView.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/orange/RaytraceImager.cu b/src/orange/RaytraceImager.cu new file mode 100644 index 0000000000..6083f59135 --- /dev/null +++ b/src/orange/RaytraceImager.cu @@ -0,0 +1,24 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 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/RaytraceImager.cu +//---------------------------------------------------------------------------// +#include "RaytraceImager.hh" + +#include "geocel/rasterize/RaytraceImager.cuda.t.hh" + +#include "OrangeData.hh" +#include "OrangeGeoTraits.hh" +#include "OrangeParams.hh" +#include "OrangeTrackView.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/orange/RaytraceImager.hh b/src/orange/RaytraceImager.hh new file mode 100644 index 0000000000..4d799bb96b --- /dev/null +++ b/src/orange/RaytraceImager.hh @@ -0,0 +1,21 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/RaytraceImager.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "geocel/rasterize/RaytraceImager.hh" + +#include "OrangeGeoTraits.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// + +extern template class RaytraceImager; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/test/celeritas/user/Diagnostic.test.cc b/test/celeritas/user/Diagnostic.test.cc index 3b6e1deb3c..ace341e727 100644 --- a/test/celeritas/user/Diagnostic.test.cc +++ b/test/celeritas/user/Diagnostic.test.cc @@ -172,23 +172,25 @@ TEST_F(TestEm3DiagnosticTest, host) if (this->is_ci_build() && CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_VECGEOM) { - static size_type const expected_nonzero_action_counts[] = {121ul, - 389ul, - 480ul, - 16ul, - 57ul, - 1024ul, - 276ul, - 290ul, - 1798ul, - 15u, - 19u, - 1171ul, - 1541ul, - 572ul, - 86ul, - 26ul, - 311ul}; + static size_type const expected_nonzero_action_counts[] = { + 121u, + 389u, + 480u, + 16u, + 57u, + 1024u, + 276u, + 290u, + 1798u, + 15u, + 19u, + 1171u, + 1541u, + 572u, + 86u, + 26u, + 311u, + }; EXPECT_VEC_EQ(expected_nonzero_action_counts, result.nonzero_action_counts); @@ -241,17 +243,11 @@ TEST_F(TestEm3DiagnosticTest, TEST_IF_CELER_DEVICE(device)) if (CELERITAS_USE_JSON) { - EXPECT_EQ( - "{\"_index\":[\"particle\",\"action\"],\"actions\":[[0,0,0,0," - "0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,9,0],[0,0,0,996,0,0,2,0,0,0," - "0,20,509,0,0,0,0,0,0,0,521,0],[0,0,0,902,0,0,10,0,0,0,9,20," - "577,0,0,0,0,0,0,0,518,0]]}", + EXPECT_JSON_EQ( + R"json({"_category":"result","_index":["particle","action"],"_label":"action-diagnostic","actions":[[0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,9,0],[0,0,0,996,0,0,2,0,0,0,0,20,509,0,0,0,0,0,0,0,521,0],[0,0,0,902,0,0,10,0,0,0,9,20,577,0,0,0,0,0,0,0,518,0]]})json", this->action_output()); - EXPECT_EQ( - "{\"_index\":[\"particle\",\"num_steps\"],\"steps\":[[0,0,0,0," - "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0," - "0,0,0,0,0,0,0,0,0,0,0],[0,0,5,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0," - "0,0,0,0]]}", + EXPECT_JSON_EQ( + R"json({"_category":"result","_index":["particle","num_steps"],"_label":"step-diagnostic","steps":[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,5,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]})json", this->step_output()); } } diff --git a/test/geocel/MockGeoTrackView.hh b/test/geocel/MockGeoTrackView.hh index dabd0379aa..11469dec4f 100644 --- a/test/geocel/MockGeoTrackView.hh +++ b/test/geocel/MockGeoTrackView.hh @@ -76,7 +76,13 @@ class MockGeoTrackView int init_count() const { return init_count_; } private: + // Physical state GeoTrackInitializer state_; + + // Logical state + int volume_id_{-1}; + bool on_boundary_{false}; + real_type next_step_{}; int init_count_{0}; @@ -93,20 +99,19 @@ MockGeoTrackView::operator=(Initializer_t const& init) { ++init_count_; this->state_ = init; + volume_id_ = std::floor(this->z()); + on_boundary_ = false; return *this; } //---------------------------------------------------------------------------// /*! * Get the volume ID in the current cell. - * - * XXX this doesn't correctly handle the logical change in state for boundary - * crossing. */ CELER_FUNCTION VolumeId MockGeoTrackView::volume_id() const { CELER_EXPECT(!this->is_outside()); - return VolumeId{static_cast(std::floor(this->z()))}; + return VolumeId{static_cast(volume_id_)}; } //---------------------------------------------------------------------------// @@ -115,7 +120,7 @@ CELER_FUNCTION VolumeId MockGeoTrackView::volume_id() const */ CELER_FUNCTION bool MockGeoTrackView::is_outside() const { - return this->z() <= 0; + return volume_id_ < 0; } //---------------------------------------------------------------------------// @@ -124,7 +129,7 @@ CELER_FUNCTION bool MockGeoTrackView::is_outside() const */ CELER_FUNCTION bool MockGeoTrackView::is_on_boundary() const { - return soft_mod(this->z(), real_type{1}); + return on_boundary_; } //---------------------------------------------------------------------------// @@ -138,16 +143,10 @@ CELER_FUNCTION Propagation MockGeoTrackView::find_next_step(real_type max_step) real_type z = state_.pos[2]; real_type w = state_.dir[2]; - real_type next_surf{100000}; - if (w > 0) - { - next_surf = (1 - std::fmod(z, real_type{1})) / w; - } - else if (w < 0) - { - next_surf = std::fmod(z, real_type{1}) / -w; - } - next_step_ = std::fmin(next_surf, max_step); + + int next_z_plane = volume_id_ + (w > 0); + next_step_ + = std::fmin((static_cast(next_z_plane) - z) / w, max_step); Propagation result; result.boundary = next_step_ < max_step; @@ -167,6 +166,7 @@ CELER_FUNCTION void MockGeoTrackView::move_to_boundary() { // Move next step this->move_internal(next_step_); + on_boundary_ = true; next_step_ = 0; CELER_ENSURE(this->is_on_boundary()); @@ -182,6 +182,10 @@ CELER_FUNCTION void MockGeoTrackView::cross_boundary() { CELER_EXPECT(this->is_on_boundary()); + // Update volume ID + real_type w = state_.dir[2]; + volume_id_ += (w > 0 ? 1 : -1); + // Make sure we're exactly on the value this->state_.pos[2] = std::round(this->state_.pos[2]); @@ -201,6 +205,7 @@ CELER_FUNCTION void MockGeoTrackView::move_internal(real_type dist) axpy(dist, state_.dir, &state_.pos); next_step_ -= dist; + on_boundary_ = false; } //---------------------------------------------------------------------------// diff --git a/test/geocel/rasterize/Raytracer.test.cc b/test/geocel/rasterize/Raytracer.test.cc index e8dae986b2..a45a861738 100644 --- a/test/geocel/rasterize/Raytracer.test.cc +++ b/test/geocel/rasterize/Raytracer.test.cc @@ -31,15 +31,22 @@ TEST(MockGeo, tracking) { MockGeoTrackView geo; geo = GeoTrackInitializer{{0, 0, 5.25}, {0, 0, 1}}; + EXPECT_EQ(5, geo.volume_id().get()); auto next_step = geo.find_next_step(2.0); EXPECT_TRUE(next_step.boundary); EXPECT_SOFT_EQ(0.75, next_step.distance); geo.move_to_boundary(); + EXPECT_EQ(5, geo.volume_id().get()); EXPECT_REAL_EQ(6.0, geo.pos()[2]); + geo.cross_boundary(); + EXPECT_EQ(6, geo.volume_id().get()); next_step = geo.find_next_step(1.5); EXPECT_TRUE(next_step.boundary); EXPECT_SOFT_EQ(1.0, next_step.distance); geo.move_to_boundary(); + EXPECT_EQ(6, geo.volume_id().get()); + geo.cross_boundary(); + EXPECT_EQ(7, geo.volume_id().get()); next_step = geo.find_next_step(0.5); EXPECT_FALSE(next_step.boundary); EXPECT_SOFT_EQ(0.5, next_step.distance); @@ -49,11 +56,13 @@ TEST(MockGeo, tracking) EXPECT_SOFT_EQ(0.75, next_step.distance); geo = GeoTrackInitializer{{0, 0, 4.75}, make_unit_vector(Real3{0, 0, -1})}; + EXPECT_EQ(4, geo.volume_id().get()); next_step = geo.find_next_step(100.0); EXPECT_TRUE(next_step.boundary); EXPECT_SOFT_EQ(0.75, next_step.distance); geo = GeoTrackInitializer{{0, 0, 9.75}, make_unit_vector(Real3{0, 4, -3})}; + EXPECT_EQ(9, geo.volume_id().get()); next_step = geo.find_next_step(100.0); EXPECT_TRUE(next_step.boundary); EXPECT_SOFT_EQ(0.75 * 5.0 / 3.0, next_step.distance); @@ -85,12 +94,15 @@ TEST_F(RaytracerTest, exact) EXPECT_EQ(0, geo.init_count()); Raytracer trace{geo, calc_id, line}; + // Position doesn't change until the next pixel EXPECT_EQ(32, trace(0)); EXPECT_EQ(1, geo.init_count()); - EXPECT_VEC_SOFT_EQ((Real3{0, 10.5, 33}), geo.pos()); + EXPECT_VEC_SOFT_EQ((Real3{0, 10.5, 32}), geo.pos()); + EXPECT_EQ(33, trace(1)); - EXPECT_VEC_SOFT_EQ((Real3{0, 10.5, 34}), geo.pos()); + EXPECT_VEC_SOFT_EQ((Real3{0, 10.5, 33}), geo.pos()); EXPECT_EQ(1, geo.init_count()); + EXPECT_EQ(40, trace(8)); EXPECT_EQ(2, geo.init_count()); } diff --git a/test/orange/CMakeLists.txt b/test/orange/CMakeLists.txt index 12d0e91a4e..c018e838d4 100644 --- a/test/orange/CMakeLists.txt +++ b/test/orange/CMakeLists.txt @@ -46,6 +46,7 @@ celeritas_add_test(BoundingBoxUtils.test.cc) celeritas_add_test(MatrixUtils.test.cc) celeritas_add_test(OrangeTypes.test.cc) celeritas_add_device_test(Orange) +celeritas_add_test(RaytraceImager.test.cc GPU) celeritas_add_test(detail/UniverseIndexer.test.cc) diff --git a/test/orange/RaytraceImager.test.cc b/test/orange/RaytraceImager.test.cc new file mode 100644 index 0000000000..47303afe1b --- /dev/null +++ b/test/orange/RaytraceImager.test.cc @@ -0,0 +1,254 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/RaytraceImager.test.cc +//---------------------------------------------------------------------------// +#include "orange/RaytraceImager.hh" + +#include "corecel/cont/Span.hh" +#include "geocel/rasterize/Image.hh" + +#include "OrangeGeoTestBase.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// +std::string to_ascii(ImageInterface const& image, Span id_to_char) +{ + ImageParams const& params = *image.params(); + + // Allocate destination space + std::vector pixels(params.num_pixels(), -1); + std::string result(params.num_pixels() + 2 * params.num_lines() + 1, ' '); + + // Copy image to our array + image.copy_to_host(make_span(pixels)); + + // Convert image to pixels + auto iter = result.begin(); + *iter++ = '\n'; + int num_cols = params.scalars().dims[1]; + for (int vol_id : pixels) + { + *iter++ = [&] { + if (vol_id < 0) + return ' '; + if (vol_id < static_cast(id_to_char.size())) + return id_to_char[vol_id]; + return 'x'; + }(); + + CELER_ASSERT(iter != result.end()); + if (--num_cols == 0) + { + *iter++ = '|'; + CELER_ASSERT(iter != result.end()); + *iter++ = '\n'; + num_cols = params.scalars().dims[1]; + } + } + CELER_ASSERT(iter == result.end()); + return result; +} + +//---------------------------------------------------------------------------// +struct TwoVolumeTest +{ + //! Name of the test + static inline char const* name = "two_volume"; + //! Geo params arguments + static auto geometry_input() + { + return OrangeGeoTestBase::TwoVolInput{1.0}; + } + //! Image params input + static ImageInput image_input() + { + ImageInput result; + result.lower_left = {0, 0, 0}; + result.upper_right = {1, 1, 0}; + result.rightward = {1, 0, 0}; + result.vertical_pixels = 8; + result.horizontal_divisor = 1; + return result; + } + //! Mapping of volume ID to character + static constexpr char const id_to_char[] = {' ', 'x'}; + //! Expected image + static constexpr char const expected_image[] = R"( +xxx | +xxxxx | +xxxxxx | +xxxxxxx | +xxxxxxx | +xxxxxxxx| +xxxxxxxx| +xxxxxxxx| +)"; +}; + +//---------------------------------------------------------------------------// +struct TwoVolumeTestBackward : public TwoVolumeTest +{ + static inline char const* name = "two_volume backward"; + static ImageInput image_input() + { + ImageInput result; + result.lower_left = {1, 0, 0}; + result.upper_right = {0, 1, 0}; + result.rightward = {-1, 0, 0}; + result.vertical_pixels = 8; + result.horizontal_divisor = 1; + return result; + } + // NOTE: image is different because the trace starts outside the geometry + static constexpr char const expected_image[] = R"( + xx| + xxxx| + xxxxx| + xxxxxx| + xxxxxxx| + xxxxxxx| + xxxxxxx| + xxxxxxx| +)"; +}; + +//---------------------------------------------------------------------------// +struct UniversesTest +{ + //! Name of the test + static inline char const* name = "universes"; + //! Geo params arguments + static auto geometry_input() { return "universes.org.json"; } + //! Image params input + static ImageInput image_input() + { + // NOTE: due to axis-aligned boundaries, we can't start the raytrace on + // the outer box + ImageInput result; + result.lower_left = {-1.9, -5.9, 0.25}; + result.upper_right = {8.1, 4.1, 0.25}; + result.rightward = {1, 0, 0}; + result.vertical_pixels = 20; + result.horizontal_divisor = 2; + return result; + } + /*! + * Mapping of volume ID to character. + * + * - ` `: `[EXTERIOR]` volume (should not appear since ID should be -1 when + * outside) + * - `-`: volume replaced by child universe (should never be "final" + * volume) + * - `abc`: universes named as such + * - `BJP`: bobby, johnny, patty + */ + static constexpr char const id_to_char[] = " --BJ -abc P"; + //! Expected image + static constexpr char const expected_image[] = R"( +JJJJJJJJJJJJJJJJJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +JJJJBBBBBBBBBBBBJJJJ| +JJJJBBBBBBBBBBBBJJJJ| +JJJJBBBBBBBBBBBBJJJJ| +JJJJBBBBBBBBBBBBJJJJ| +JJJJccccccccccccJJJJ| +JJJJccccccccccccJJJJ| +JJJJccaaaabbbbccJJJJ| +JJJJccaaaabbbbccJJJJ| +JJJJccaaaabbbbccJJJJ| +JJJJccaaaabbbbccJJJJ| +JJJJccccccccccccJJJJ| +JJJJPcccccccccccJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +JJJJJJJJJJJJJJJJJJJJ| +)"; +}; + +//---------------------------------------------------------------------------// +template +class RaytraceImagerTest : public OrangeGeoTestBase +{ + protected: + using Initializer_t = GeoTrackInitializer; + + real_type unit_length() const override { return 1; } + + void SetUp() override + { + this->build_geometry(P::geometry_input()); + img_params_ = std::make_shared(P::image_input()); + } + + ImageParams const& img_params() const { return *img_params_; } + + template + void test_image() const; + + private: + std::shared_ptr img_params_; +}; + +template +template +void RaytraceImagerTest

::test_image() const +{ + RaytraceImager raytrace_image{this->geometry()}; + + Image image(img_params_); + + // Raytrace + raytrace_image(&image); + auto actual = to_ascii(image, make_span(P::id_to_char)); + EXPECT_EQ(P::expected_image, actual) + << "static constexpr char const expected_image[] = R\"(" << actual + << ")\";\n"; + + // Raytrace again, hopefully reusing geometry cache + raytrace_image(&image); + auto again = to_ascii(image, make_span(P::id_to_char)); + EXPECT_EQ(actual, again); +} + +//---------------------------------------------------------------------------// +using RaytraceTypes + = ::testing::Types; + +struct TestToString +{ + template + static std::string GetName(int) + { + return U::name; + } +}; + +TYPED_TEST_SUITE(RaytraceImagerTest, RaytraceTypes, TestToString); + +TYPED_TEST(RaytraceImagerTest, host) +{ + this->template test_image(); +} + +#if CELER_USE_DEVICE +TYPED_TEST(RaytraceImagerTest, device) +#else +TYPED_TEST(RaytraceImagerTest, DISABLED_device) +#endif +{ + this->template test_image(); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas From d6720d6fdd9dbb127e57ad698690b864118461fc Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 26 Apr 2024 12:48:25 -0400 Subject: [PATCH 25/59] Fix gentrap orientation (#1206) * Add translated test * Add has_orientation function * Add validation for orientation * Avoid debug failures when interior and exterior bboxes are both null * Fix polygon orientation --- src/orange/orangeinp/ConvexRegion.cc | 58 ++++++++----- .../orangeinp/detail/ConvexSurfaceState.cc | 4 +- src/orange/orangeinp/detail/PolygonUtils.hh | 23 +++++- test/orange/orangeinp/ConvexRegion.test.cc | 82 ++++++++++++++++--- .../orangeinp/detail/PolygonUtils.test.cc | 22 ++++- 5 files changed, 150 insertions(+), 39 deletions(-) diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/ConvexRegion.cc index c2a08bd252..ddfeb7ab3f 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/ConvexRegion.cc @@ -12,6 +12,7 @@ #include "corecel/Constants.hh" #include "corecel/cont/Range.hh" #include "corecel/io/JsonPimpl.hh" +#include "corecel/math/SoftEqual.hh" #include "geocel/BoundingBox.hh" #include "geocel/Types.hh" #include "orange/orangeinp/detail/PolygonUtils.hh" @@ -323,19 +324,32 @@ GenTrap::GenTrap(real_type halfz, VecReal2 const& lo, VecReal2 const& hi) CELER_VALIDATE(hz_ > 0, << "nonpositive halfheight: " << hz_); CELER_VALIDATE(lo_.size() >= 3 && lo_.size() <= 4, << "invalid number of vertices (" << lo_.size() - << ") for -Z polygon"); - CELER_VALIDATE(hi_.size() >= 3 && hi_.size() <= 4, - << "invalid number of vertices <" << hi_.size() - << ") for +Z polygon"); + << ") for -z polygon"); + CELER_VALIDATE(hi_.size() == lo_.size(), + << "incompatible number of vertices (" << hi_.size() + << ") for +z polygon: expected " << lo_.size()); CELER_VALIDATE(lo_.size() >= 3 || hi_.size() >= 3, - << "not enough vertices for both of the +Z/-Z polygons."); + << "not enough vertices for both of the +z/-z polygons."); - // Input vertices must be arranged in a CCW order + // Input vertices must be arranged in the same counter/clockwise order + // and be convex + using detail::calc_orientation; + constexpr auto cw = detail::Orientation::clockwise; CELER_VALIDATE(detail::is_convex(make_span(lo_)), - << "-Z polygon is not convex"); + << "-z polygon is not convex"); CELER_VALIDATE(detail::is_convex(make_span(hi_)), - << "+Z polygon is not convex"); + << "+z polygon is not convex"); + CELER_VALIDATE(calc_orientation(lo_[0], lo_[1], lo_[2]) + == calc_orientation(hi_[0], hi_[1], hi_[2]), + << "-z and +z polygons have different orientations"); + if (calc_orientation(lo_[0], lo_[1], lo_[2]) == cw) + { + // Reverse point orders so it's counterclockwise, needed for vectors to + // point outward + std::reverse(lo_.begin(), lo_.end()); + std::reverse(hi_.begin(), hi_.end()); + } // TODO: Temporarily ensure that all side faces are planar for (auto i : range(lo_.size())) @@ -367,22 +381,24 @@ void GenTrap::build(ConvexSurfaceBuilder& insert_surface) const // Build the side planes for (auto i : range(lo_.size())) { + // Viewed from the outside of the trapezoid, the points on the polygon + // here are from the lower left counterclockwise to the upper right auto j = (i + 1) % lo_.size(); - Real3 const a{lo_[i][0], lo_[i][1], -hz_}; - Real3 const b{lo_[j][0], lo_[j][1], -hz_}; - Real3 const c{hi_[j][0], hi_[j][1], hz_}; - Real3 const d{hi_[i][0], hi_[i][1], hz_}; - - // Calculate plane parameters - auto normal = make_unit_vector(cross_product(b - a, c - b)); - auto offset = dot_product(d, normal); - - // *Temporarily* throws if a side face is not planar - CELER_ASSERT(celeritas::orangeinp::detail::is_planar(a, b, c, d)); - CELER_ASSERT(SoftZero{}(dot_product(d - a, normal))); + Real3 const ilo{lo_[i][0], lo_[i][1], -hz_}; + Real3 const jlo{lo_[j][0], lo_[j][1], -hz_}; + Real3 const jhi{hi_[j][0], hi_[j][1], hz_}; + Real3 const ihi{hi_[i][0], hi_[i][1], hz_}; + + // Calculate outward normal by taking the cross product of the edges + auto normal = make_unit_vector(cross_product(jlo - ilo, ihi - ilo)); + // Assert that the surface is (for now!) not twisted + CELER_ASSERT(soft_equal( + dot_product(make_unit_vector(cross_product(ihi - jhi, jlo - jhi)), + normal), + real_type{1})); // Insert the plane - insert_surface(Sense::inside, Plane{normal, offset}); + insert_surface(Sense::inside, Plane{normal, ilo}); } } diff --git a/src/orange/orangeinp/detail/ConvexSurfaceState.cc b/src/orange/orangeinp/detail/ConvexSurfaceState.cc index 4372886c16..1d0176d48f 100644 --- a/src/orange/orangeinp/detail/ConvexSurfaceState.cc +++ b/src/orange/orangeinp/detail/ConvexSurfaceState.cc @@ -25,7 +25,9 @@ namespace detail BoundingZone calc_merged_bzone(ConvexSurfaceState const& css) { CELER_EXPECT(css.transform); - CELER_EXPECT(encloses(css.local_bzone.exterior, css.local_bzone.interior)); + CELER_EXPECT( + (!css.local_bzone.exterior && !css.local_bzone.interior) + || encloses(css.local_bzone.exterior, css.local_bzone.interior)); CELER_EXPECT(!css.local_bzone.negated); CELER_EXPECT(!css.global_bzone.negated); diff --git a/src/orange/orangeinp/detail/PolygonUtils.hh b/src/orange/orangeinp/detail/PolygonUtils.hh index 756b8d6538..2ebb9f4aae 100644 --- a/src/orange/orangeinp/detail/PolygonUtils.hh +++ b/src/orange/orangeinp/detail/PolygonUtils.hh @@ -44,7 +44,7 @@ enum class Orientation /*! * Find orientation of ordered vertices in 2D coordinates. */ -CELER_FORCEINLINE_FUNCTION Orientation orientation(Real2 const& a, +inline CELER_FUNCTION Orientation calc_orientation(Real2 const& a, Real2 const& b, Real2 const& c) { @@ -54,6 +54,26 @@ CELER_FORCEINLINE_FUNCTION Orientation orientation(Real2 const& a, : Orientation::collinear; } +//---------------------------------------------------------------------------// +/*! + * Test whether a 2D polygon has the given orientation. + * + * The list of input corners must have at least 3 points to be a polygon. + */ +inline CELER_FUNCTION bool +has_orientation(Span const& corners, Orientation o) +{ + CELER_EXPECT(corners.size() > 2); + for (auto i : range(corners.size())) + { + auto j = (i + 1) % corners.size(); + auto k = (i + 2) % corners.size(); + if (calc_orientation(corners[i], corners[j], corners[k]) != o) + return false; + } + return true; +} + //---------------------------------------------------------------------------// /*! * Check if a 2D polygon is convex. @@ -65,6 +85,7 @@ CELER_FUNCTION bool is_convex(Span const& corners, bool degen_ok = false) { CELER_EXPECT(!corners.empty()); + // The cross product of all vector pairs corresponding to ordered // consecutive segments has to be positive. auto crossp = [&](Real2 const& v1, Real2 const& v2) { diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 8e4667076e..b888315c9f 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -83,8 +83,16 @@ auto ConvexRegionTest::test(ConvexRegionInterface const& r, ConvexSurfaceBuilder insert_surface{&unit_builder_, &css}; r.build(insert_surface); - EXPECT_TRUE(encloses(css.local_bzone.exterior, css.local_bzone.interior)); - EXPECT_TRUE(encloses(css.global_bzone.exterior, css.global_bzone.interior)); + if (css.local_bzone.exterior || css.local_bzone.interior) + { + EXPECT_TRUE( + encloses(css.local_bzone.exterior, css.local_bzone.interior)); + } + if (css.global_bzone.exterior || css.global_bzone.interior) + { + EXPECT_TRUE( + encloses(css.global_bzone.exterior, css.global_bzone.interior)); + } // Intersect the given surfaces NodeId node_id @@ -495,22 +503,72 @@ TEST_F(GenTrapTest, triang_prism) auto result = this->test( GenTrap(3, {{-1, -1}, {-1, 1}, {2, 0}}, {{-1, -1}, {-1, 1}, {2, 0}})); - static char const expected_node[] = "all(+0, -1, -2, +3, +4)"; - static char const* const expected_surfaces[] - = {"Plane: z=-3", - "Plane: z=3", - "Plane: x=-1", - "Plane: n={0.31623,0.94868,0}, d=0.63246", - "Plane: n={0.31623,-0.94868,0}, d=0.63246"}; + static char const expected_node[] = "all(+0, -1, -2, +3, -4)"; + static char const* const expected_surfaces[] = { + "Plane: z=-3", + "Plane: z=3", + "Plane: n={0.31623,0.94868,-0}, d=0.63246", + "Plane: x=-1", + "Plane: n={0.31623,-0.94868,0}, d=0.63246", + }; EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -3}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{-1, inf, 3}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-1, -inf, -3}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); +} + +TEST_F(GenTrapTest, trapezoid) +{ + auto result + = this->test(GenTrap(40, + {{-19, -30}, {-19, 30}, {21, 30}, {21, -30}}, + {{-21, -30}, {-21, 30}, {19, 30}, {19, -30}})); + + static char const expected_node[] = "all(+0, -1, -2, -3, +4, +5)"; + static char const* const expected_surfaces[] = { + "Plane: z=-40", + "Plane: z=40", + "Plane: n={0.99969,-0,0.024992}, d=19.994", + "Plane: y=30", + "Plane: n={0.99969,0,0.024992}, d=-19.994", + "Plane: y=-30", + }; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -30, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, 30, 40}), result.exterior.upper()); +} + +TEST_F(GenTrapTest, trapezoid_trans) +{ + // trapezoid but translated -30, -30 + auto result + = this->test(GenTrap(40, + {{-49, -60}, {-49, 0}, {-9, 0}, {-9, -60}}, + {{-51, -60}, {-51, 0}, {-11, 0}, {-11, -60}})); + + static char const expected_node[] = "all(+0, -1, -2, -3, +4, +5)"; + static char const* const expected_surfaces[] = { + "Plane: z=-40", + "Plane: z=40", + "Plane: n={0.99969,-0,0.024992}, d=-9.9969", + "Plane: y=0", + "Plane: n={0.99969,0,0.024992}, d=-49.984", + "Plane: y=-60", + }; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -60, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, 0, 40}), result.exterior.upper()); } -TEST_F(GenTrapTest, CCWtrap) +TEST_F(GenTrapTest, trapezoid_ccw) { auto result = this->test(GenTrap(40, diff --git a/test/orange/orangeinp/detail/PolygonUtils.test.cc b/test/orange/orangeinp/detail/PolygonUtils.test.cc index 9960d9477b..e9c535512a 100644 --- a/test/orange/orangeinp/detail/PolygonUtils.test.cc +++ b/test/orange/orangeinp/detail/PolygonUtils.test.cc @@ -33,16 +33,30 @@ using constants::pi; // TESTS //---------------------------------------------------------------------------// -TEST(PolygonUtilsTest, orientation) +TEST(PolygonUtilsTest, calc_orientation) { - EXPECT_TRUE(orientation(Real2{0, 0}, Real2{4, 4}, Real2{1, 2}) + EXPECT_TRUE(calc_orientation(Real2{0, 0}, Real2{4, 4}, Real2{1, 2}) == Orientation::counterclockwise); - EXPECT_TRUE(orientation(Real2{0, 0}, Real2{4, 4}, Real2{2, 1}) + EXPECT_TRUE(calc_orientation(Real2{0, 0}, Real2{4, 4}, Real2{2, 1}) == Orientation::clockwise); - EXPECT_TRUE(orientation(Real2{0, 0}, Real2{4, 4}, Real2{2, 2}) + EXPECT_TRUE(calc_orientation(Real2{0, 0}, Real2{4, 4}, Real2{2, 2}) == Orientation::collinear); } +TEST(PolygonUtilsTest, has_orientation) +{ + EXPECT_TRUE(has_orientation( + make_span(VecReal2{{-19, -30}, {-19, 30}, {21, 30}, {21, -30}}), + Orientation::clockwise)); + EXPECT_FALSE(has_orientation( + make_span(VecReal2{{-19, -30}, {-19, 30}, {21, 30}, {21, -30}}), + Orientation::counterclockwise)); + + EXPECT_TRUE(has_orientation( + make_span(VecReal2{{-2, -2}, {0, -2}, {0, 0}, {-2, 0}}), + Orientation::counterclockwise)); +} + TEST(PolygonUtilsTest, convexity) { VecReal2 cw{{1, 1}, {1, 2}, {2, 2}, {2, 1}}; From 772d57c01eb0923b7d2f510e6dcaec2ec22b6173 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Sun, 28 Apr 2024 08:13:36 -0400 Subject: [PATCH 26/59] Refactor `ActionSequence` to be templated on Params (#1209) --- src/celeritas/global/Stepper.hh | 5 +- src/celeritas/global/detail/ActionSequence.cc | 55 +++++++++---------- src/celeritas/global/detail/ActionSequence.hh | 27 +++++++-- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/celeritas/global/Stepper.hh b/src/celeritas/global/Stepper.hh index b92d892f10..60fb8a2a3e 100644 --- a/src/celeritas/global/Stepper.hh +++ b/src/celeritas/global/Stepper.hh @@ -30,6 +30,7 @@ struct Primary; namespace detail { +template class ActionSequence; } @@ -78,7 +79,7 @@ class StepperInterface //!@{ //! \name Type aliases using Input = StepperInput; - using ActionSequence = detail::ActionSequence; + using ActionSequence = detail::ActionSequence; using SpanConstPrimary = Span; using result_type = StepperResult; //!@} @@ -157,7 +158,7 @@ class Stepper final : public StepperInterface private: // Params and call sequence std::shared_ptr params_; - std::shared_ptr actions_; + std::shared_ptr actions_; // State data CoreState state_; }; diff --git a/src/celeritas/global/detail/ActionSequence.cc b/src/celeritas/global/detail/ActionSequence.cc index 04e4d1c471..44d7cec67b 100644 --- a/src/celeritas/global/detail/ActionSequence.cc +++ b/src/celeritas/global/detail/ActionSequence.cc @@ -20,8 +20,8 @@ #include "corecel/sys/ScopedProfiling.hh" #include "corecel/sys/Stopwatch.hh" #include "corecel/sys/Stream.hh" +#include "celeritas/global/CoreParams.hh" -#include "ParamsTraits.hh" #include "../ActionInterface.hh" #include "../ActionRegistry.hh" #include "../CoreState.hh" @@ -34,22 +34,26 @@ namespace detail /*! * Construct from an action registry and sequence options. */ -ActionSequence::ActionSequence(ActionRegistry const& reg, Options options) +template +ActionSequence::ActionSequence(ActionRegistry const& reg, + Options options) : options_(std::move(options)) { + actions_.reserve(reg.num_actions()); // Loop over all action IDs for (auto aidx : range(reg.num_actions())) { // Get abstract action shared pointer and see if it's explicit auto const& base = reg.action(ActionId{aidx}); - if (auto expl - = std::dynamic_pointer_cast(base)) + using element_type = typename SPConstSpecializedExplicit::element_type; + if (auto expl = std::dynamic_pointer_cast(base)) { // Add explicit action to our array actions_.push_back(std::move(expl)); } } + begin_run_.reserve(reg.mutable_actions().size()); // Loop over all mutable actions for (auto const& base : reg.mutable_actions()) { @@ -63,7 +67,8 @@ ActionSequence::ActionSequence(ActionRegistry const& reg, Options options) // Sort actions by increasing order (and secondarily, increasing IDs) std::sort(actions_.begin(), actions_.end(), - [](SPConstExplicit const& a, SPConstExplicit const& b) { + [](SPConstSpecializedExplicit const& a, + SPConstSpecializedExplicit const& b) { return std::make_tuple(a->order(), a->action_id()) < std::make_tuple(b->order(), b->action_id()); }); @@ -78,8 +83,9 @@ ActionSequence::ActionSequence(ActionRegistry const& reg, Options options) /*! * Initialize actions and states. */ +template template -void ActionSequence::begin_run(CoreParams const& params, CoreState& state) +void ActionSequence::begin_run(Params const& params, State& state) { for (auto const& sp_action : begin_run_) { @@ -92,15 +98,10 @@ void ActionSequence::begin_run(CoreParams const& params, CoreState& state) /*! * Call all explicit actions with host or device data. */ -template class State, MemSpace M> -void ActionSequence::execute(Params const& params, State& state) +template +template +void ActionSequence::execute(Params const& params, State& state) { - using ExplicitAction = typename ParamsTraits::ExplicitAction; - - static_assert( - std::is_same_v, typename ParamsTraits::template State>, - "The Params and State type are not matching."); - [[maybe_unused]] Stream::StreamT stream = nullptr; if (M == MemSpace::device && options_.sync) { @@ -114,9 +115,7 @@ void ActionSequence::execute(Params const& params, State& state) { ScopedProfiling profile_this{actions_[i]->label()}; Stopwatch get_time; - auto const& concrete_action - = dynamic_cast(*actions_[i]); - concrete_action.execute(params, state); + actions_[i]->execute(params, state); if (M == MemSpace::device) { CELER_DEVICE_CALL_PREFIX(StreamSynchronize(stream)); @@ -127,12 +126,10 @@ void ActionSequence::execute(Params const& params, State& state) else { // Just loop over the actions - for (SPConstExplicit const& sp_action : actions_) + for (auto const& sp_action : actions_) { ScopedProfiling profile_this{sp_action->label()}; - auto const& concrete_action - = dynamic_cast(*sp_action); - concrete_action.execute(params, state); + sp_action->execute(params, state); } } } @@ -141,15 +138,17 @@ void ActionSequence::execute(Params const& params, State& state) // Explicit template instantiation //---------------------------------------------------------------------------// -template void -ActionSequence::begin_run(CoreParams const&, CoreState&); -template void -ActionSequence::begin_run(CoreParams const&, CoreState&); +template class ActionSequence; + +template void ActionSequence::begin_run(CoreParams const&, + State&); +template void ActionSequence::begin_run(CoreParams const&, + State&); template void -ActionSequence::execute(CoreParams const&, CoreState&); -template void -ActionSequence::execute(CoreParams const&, CoreState&); +ActionSequence::execute(CoreParams const&, State&); +template void ActionSequence::execute(CoreParams const&, + State&); // TODO: add explicit template instantiation of execute for optical data diff --git a/src/celeritas/global/detail/ActionSequence.hh b/src/celeritas/global/detail/ActionSequence.hh index 87cae4328c..b70373179b 100644 --- a/src/celeritas/global/detail/ActionSequence.hh +++ b/src/celeritas/global/detail/ActionSequence.hh @@ -8,10 +8,12 @@ #pragma once #include +#include #include #include "corecel/Types.hh" +#include "ParamsTraits.hh" #include "../ActionInterface.hh" #include "../CoreTrackDataFwd.hh" @@ -30,18 +32,31 @@ namespace detail * TODO accessors here are used by diagnostic output from celer-sim etc.; * perhaps make this public or add a diagnostic output for it? */ +template class ActionSequence { public: //!@{ //! \name Type aliases + template + using State = typename ParamsTraits::template State; + using SpecializedExplicitAction = + typename ParamsTraits::ExplicitAction; using SPBegin = std::shared_ptr; - using SPConstExplicit = std::shared_ptr; + using SPConstSpecializedExplicit + = std::shared_ptr; using VecBeginAction = std::vector; - using VecExplicitAction = std::vector; + using VecSpecializedExplicitAction + = std::vector; using VecDouble = std::vector; //!@} + // Verify that we have a valid explicit action type for the given Params + static_assert( + std::is_base_of_v, + "ParamTraits explicit action must be derived from " + "ExplicitActionInterface"); + //! Construction/execution options struct Options { @@ -56,10 +71,10 @@ class ActionSequence // Launch all actions with the given memory space. template - void begin_run(CoreParams const& params, CoreState& state); + void begin_run(Params const& params, State& state); // Launch all actions with the given memory space. - template class State, MemSpace M> + template void execute(Params const&, State& state); //// ACCESSORS //// @@ -71,7 +86,7 @@ class ActionSequence VecBeginAction const& begin_run_actions() const { return begin_run_; } //! Get the ordered vector of actions in the sequence - VecExplicitAction const& actions() const { return actions_; } + VecSpecializedExplicitAction const& actions() const { return actions_; } //! Get the corresponding accumulated time, if 'sync' or host called VecDouble const& accum_time() const { return accum_time_; } @@ -79,7 +94,7 @@ class ActionSequence private: Options options_; VecBeginAction begin_run_; - VecExplicitAction actions_; + VecSpecializedExplicitAction actions_; VecDouble accum_time_; }; From 69cdb1a4d71412bb2624d5e109b77198ac6e79ac Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Mon, 29 Apr 2024 08:15:17 -0400 Subject: [PATCH 27/59] Update Frontier installation (#1208) * Fix thrust build with rocm 5.7.1 * Fix non-agnostic test name * Update frontier environment * Load miniforge for python * Ignore pr workflow for unrelated scripts * Fix loaded data and cmake flags * Use more cores * Use conda path * Unload darshan --- .github/workflows/pr.yml | 5 ++++- scripts/cmake-presets/frontier.json | 5 +++-- scripts/env/frontier.sh | 26 +++++++++++++--------- src/corecel/device_runtime_api.h | 2 ++ test/corecel/data/DeviceAllocation.test.cc | 10 ++++----- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4c55d04d3a..0c60832941 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -11,8 +11,11 @@ on: paths-ignore: - '**.rst' - '**.md' - - 'scripts/dev' + - 'scripts' - '.jenkins' + - '!scripts/cmake-presets/ci*' + - '!scripts/ci' + - '!scripts/build.sh' concurrency: group: pr-${{github.ref}}-${{github.event.number}}-${{github.workflow}} diff --git a/scripts/cmake-presets/frontier.json b/scripts/cmake-presets/frontier.json index 07b561eb91..f1b72dfa9f 100644 --- a/scripts/cmake-presets/frontier.json +++ b/scripts/cmake-presets/frontier.json @@ -22,6 +22,7 @@ "CELERITAS_TEST_RESOURCE_LOCK": {"type": "BOOL", "value": "ON"}, "CMAKE_HIP_ARCHITECTURES": {"type": "STRING", "value": "gfx90a"}, "CMAKE_HIP_FLAGS": "-munsafe-fp-atomics", + "CMAKE_CXX_FLAGS": "-Wno-unused-command-line-argument -Wall -Wextra -pedantic -fcolor-diagnostics", "CMAKE_EXE_LINKER_FLAGS": "-Wno-unused-command-line-argument", "CMAKE_HIP_FLAGS_DEBUG": "-g -ggdb -O", "CMAKE_CXX_FLAGS_RELEASE": "-O3 -DNDEBUG -march=znver3 -mtune=znver3", @@ -80,7 +81,7 @@ { "name": "base", "configurePreset": "base", - "jobs": 8, + "jobs": 64, "nativeToolOptions": ["-k0"] }, {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"} @@ -90,7 +91,7 @@ "name": "base", "configurePreset": "base", "output": {"outputOnFailure": true}, - "execution": {"noTestsAction": "error", "stopOnFailure": false, "jobs": 8} + "execution": {"noTestsAction": "error", "stopOnFailure": false, "jobs": 32} }, {"name": "ndebug", "configurePreset": "ndebug", "inherits": "base"} ] diff --git a/scripts/env/frontier.sh b/scripts/env/frontier.sh index 15b41c73c9..6108149b04 100755 --- a/scripts/env/frontier.sh +++ b/scripts/env/frontier.sh @@ -1,15 +1,19 @@ #!/bin/sh -e -_celer_base=$PROJWORK/csc404/celeritas-frontier - -# From spack compiler toolchain -module load amd/5.6.0 PrgEnv-amd/8.3.3 craype-x86-trento libfabric/1.15.2.0 cray-pmi/6.1.8 +PROJID=hep143 +_celer_view=${PROJWORK}/${PROJID}/opt-view +_tool_view=/ccs/proj/${PROJID}/opt-view +_conda=/ccs/proj/${PROJID}/conda-frontier + +module load PrgEnv-amd/8.5.0 cpe/23.12 amd/5.7.1 craype-x86-trento \ + libfabric/1.15.2.0 miniforge3/23.11.0 +# Disable warning "Using generic mem* routines instead of tuned routines" export RFE_811452_DISABLE=1 -export LD_LIBRARY_PATH=/opt/cray/pe/pmi/6.1.8/lib:$LD_LIBRARY_PATH:/opt/cray/pe/gcc-libs:/opt/cray/libfabric/1.15.2.0/lib64 -export LIBRARY_PATH=/opt/rocm-5.6.0/lib:/opt/rocm-5.6.0/lib64:$LIBRARY_PATH # Avoid linking multiple different libsci (one with openmp, one without) module unload cray-libsci +# Avoid libraries interfering with I/O +module unload darshan-runtime # Set up compilers test -n "${CRAYPE_DIR}" @@ -23,13 +27,13 @@ export CC=${CRAYPE_DIR}/bin/cc # module load craype-accel-amd-gfx90a # Set up celeritas -export SPACK_ROOT=/ccs/proj/csc404/spack-frontier -export PATH=${_celer_base}/spack/view/bin:$PATH -export CMAKE_PREFIX_PATH=${_celer_base}/spack/view:${CMAKE_PREFIX_PATH} -export MODULEPATH=${SPACK_ROOT}/share/spack/lmod/cray-sles15-x86_64/Core:${MODULEPATH} +export SPACK_ROOT=/ccs/proj/hep143/spack +export PATH=${_celer_view}/bin:${_tool_view}/bin:${_conda}/bin:$PATH +export CMAKE_PREFIX_PATH=${_celer_view}:${CMAKE_PREFIX_PATH} +export MODULEPATH=${PROJWORK}/${PROJID}/share/lmod/linux-sles15-x86_64/Core:${MODULEPATH} # Set up Geant4 data -module load geant4-data +module load geant4-data/11.0 test -n "${G4ENSDFSTATEDATA}" test -e "${G4ENSDFSTATEDATA}" diff --git a/src/corecel/device_runtime_api.h b/src/corecel/device_runtime_api.h index 17124b9c64..f7da89f0d6 100644 --- a/src/corecel/device_runtime_api.h +++ b/src/corecel/device_runtime_api.h @@ -20,8 +20,10 @@ #if CELERITAS_USE_CUDA # include +# define THRUST_DEVICE_SYSTEM THRUST_DEVICE_SYSTEM_CUDA #elif CELERITAS_USE_HIP # include +# define THRUST_DEVICE_SYSTEM THRUST_DEVICE_SYSTEM_HIP #endif #if CELERITAS_USE_CUDA || CELERITAS_USE_HIP diff --git a/test/corecel/data/DeviceAllocation.test.cc b/test/corecel/data/DeviceAllocation.test.cc index 1d58356e79..d596c62cd1 100644 --- a/test/corecel/data/DeviceAllocation.test.cc +++ b/test/corecel/data/DeviceAllocation.test.cc @@ -27,14 +27,14 @@ TEST(ConstructionTest, should_work_always) EXPECT_TRUE(alloc.empty()); } -TEST(ConstructionTest, nocuda) -{ #if !CELER_USE_DEVICE - // Can't allocate - EXPECT_THROW(DeviceAllocation(1234), DebugError); +TEST(ConstructionTest, nodevice) #else - GTEST_SKIP() << "CUDA is enabled"; +TEST(ConstructionTest, DISABLED_nodevice) #endif +{ + // Can't allocate + EXPECT_THROW(DeviceAllocation(1234), DebugError); } TEST(DeviceAllocationTest, TEST_IF_CELER_DEVICE(device)) From 2767efed3eac79e2ec69e082ac254890885e4b77 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Mon, 29 Apr 2024 23:33:32 -0400 Subject: [PATCH 28/59] Use string view for interface labels and descriptions (#1210) * Use string_view instead of string for interface labels+descriptions * Use string view in scoped profiling * Preallocate stepping counters in transport loop * Fix device build errors * Incorporate review feedback --- app/celer-g4/TimerOutput.hh | 2 +- app/celer-sim/RunnerOutput.hh | 2 +- app/celer-sim/Transporter.cc | 12 ++++++++-- src/accel/GeantSimpleCalo.cc | 2 +- src/accel/GeantSimpleCalo.hh | 2 +- src/accel/GeantStepDiagnostic.hh | 2 +- src/accel/LocalTransporter.cc | 3 +-- src/celeritas/em/model/BetheHeitlerModel.hh | 4 ++-- src/celeritas/em/model/CombinedBremModel.hh | 4 ++-- .../em/model/CoulombScatteringModel.hh | 4 ++-- src/celeritas/em/model/EPlusGGModel.hh | 4 ++-- src/celeritas/em/model/KleinNishinaModel.hh | 4 ++-- src/celeritas/em/model/LivermorePEModel.hh | 4 ++-- src/celeritas/em/model/MollerBhabhaModel.hh | 4 ++-- .../em/model/MuBremsstrahlungModel.hh | 4 ++-- src/celeritas/em/model/RayleighModel.hh | 4 ++-- .../em/model/RelativisticBremModel.hh | 4 ++-- src/celeritas/em/model/SeltzerBergerModel.hh | 4 ++-- .../em/process/BremsstrahlungProcess.cc | 2 +- .../em/process/BremsstrahlungProcess.hh | 2 +- src/celeritas/em/process/ComptonProcess.cc | 2 +- src/celeritas/em/process/ComptonProcess.hh | 2 +- .../em/process/CoulombScatteringProcess.cc | 2 +- .../em/process/CoulombScatteringProcess.hh | 2 +- .../em/process/EIonizationProcess.cc | 2 +- .../em/process/EIonizationProcess.hh | 2 +- .../em/process/EPlusAnnihilationProcess.cc | 2 +- .../em/process/EPlusAnnihilationProcess.hh | 2 +- .../em/process/GammaConversionProcess.cc | 2 +- .../em/process/GammaConversionProcess.hh | 2 +- .../em/process/PhotoelectricProcess.cc | 2 +- .../em/process/PhotoelectricProcess.hh | 2 +- src/celeritas/em/process/RayleighProcess.cc | 2 +- src/celeritas/em/process/RayleighProcess.hh | 2 +- src/celeritas/global/ActionInterface.hh | 9 +++---- src/celeritas/global/ActionLauncher.device.hh | 2 +- src/celeritas/global/ActionRegistry.cc | 2 +- src/celeritas/global/ActionRegistryOutput.hh | 2 +- .../global/KernelContextException.cc | 4 ++-- .../global/KernelContextException.hh | 5 +++- .../alongstep/AlongStepGeneralLinearAction.hh | 7 ++++-- .../alongstep/AlongStepNeutralAction.hh | 4 ++-- .../alongstep/AlongStepRZMapFieldMscAction.hh | 4 ++-- .../alongstep/AlongStepUniformMscAction.hh | 4 ++-- src/celeritas/mat/MaterialParamsOutput.hh | 2 +- .../neutron/model/ChipsNeutronElasticModel.hh | 4 ++-- .../neutron/model/NeutronInelasticModel.hh | 7 ++++-- .../neutron/process/NeutronElasticProcess.cc | 2 +- .../neutron/process/NeutronElasticProcess.hh | 2 +- .../process/NeutronInelasticProcess.cc | 2 +- .../process/NeutronInelasticProcess.hh | 2 +- src/celeritas/optical/detail/PreGenAction.cc | 2 +- src/celeritas/optical/detail/PreGenAction.hh | 7 ++++-- .../optical/detail/PreGenGatherAction.cc | 2 +- .../optical/detail/PreGenGatherAction.hh | 7 ++++-- src/celeritas/phys/ParticleParamsOutput.hh | 2 +- src/celeritas/phys/PhysicsParamsOutput.hh | 2 +- src/celeritas/phys/Process.hh | 2 +- .../track/ExtendFromPrimariesAction.hh | 4 ++-- .../track/ExtendFromSecondariesAction.cc | 2 +- .../track/ExtendFromSecondariesAction.hh | 4 ++-- src/celeritas/track/InitializeTracksAction.hh | 7 ++++-- src/celeritas/track/SortTracksAction.cc | 2 +- src/celeritas/track/SortTracksAction.hh | 4 ++-- src/celeritas/user/ActionDiagnostic.cc | 2 +- src/celeritas/user/ActionDiagnostic.hh | 4 ++-- src/celeritas/user/SimpleCalo.hh | 2 +- src/celeritas/user/StepDiagnostic.cc | 2 +- src/celeritas/user/StepDiagnostic.hh | 4 ++-- src/celeritas/user/detail/StepGatherAction.cc | 16 ++++--------- src/celeritas/user/detail/StepGatherAction.hh | 7 +++--- src/corecel/io/BuildOutput.hh | 2 +- src/corecel/io/ExceptionOutput.hh | 2 +- src/corecel/io/OutputInterface.hh | 3 ++- src/corecel/io/OutputInterfaceAdapter.hh | 2 +- src/corecel/io/OutputRegistry.cc | 2 +- src/corecel/sys/ScopedProfiling.cuda.cc | 11 +++++---- src/corecel/sys/ScopedProfiling.hh | 8 +++---- src/corecel/sys/ScopedProfiling.hip.cc | 5 +++- src/geocel/GeoParamsOutput.hh | 2 +- src/geocel/vg/VecgeomParamsOutput.hh | 2 +- src/orange/OrangeParamsOutput.hh | 2 +- test/celeritas/global/ActionRegistry.test.cc | 7 ++++-- test/celeritas/global/StepperTestBase.cc | 6 ++--- test/celeritas/phys/MockModel.cc | 24 +++++++------------ test/celeritas/phys/MockModel.hh | 6 +++-- test/celeritas/phys/MockProcess.cc | 2 +- test/celeritas/phys/MockProcess.hh | 2 +- test/celeritas/phys/Physics.test.cc | 10 ++++---- test/celeritas/track/MockInteractAction.hh | 7 ++++-- test/corecel/io/OutputRegistry.test.cc | 2 +- 91 files changed, 191 insertions(+), 165 deletions(-) diff --git a/app/celer-g4/TimerOutput.hh b/app/celer-g4/TimerOutput.hh index 6a7bbd4303..09c3917006 100644 --- a/app/celer-g4/TimerOutput.hh +++ b/app/celer-g4/TimerOutput.hh @@ -43,7 +43,7 @@ class TimerOutput final : public OutputInterface //! Category of data to write Category category() const final { return Category::result; } //! Key for the entry inside the category. - std::string label() const final { return "time"; } + std::string_view label() const final { return "time"; } // Write output to the given JSON object void output(JsonPimpl*) const final; //!@} diff --git a/app/celer-sim/RunnerOutput.hh b/app/celer-sim/RunnerOutput.hh index c99d513500..e4af77aa3e 100644 --- a/app/celer-sim/RunnerOutput.hh +++ b/app/celer-sim/RunnerOutput.hh @@ -49,7 +49,7 @@ class RunnerOutput final : public OutputInterface Category category() const final { return Category::result; } //! Name of the entry inside the category. - std::string label() const final { return "runner"; } + std::string_view label() const final { return "runner"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index b0fabb32fd..30639624c2 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -88,6 +88,15 @@ auto Transporter::operator()(SpanConstPrimary primaries) result.alive.push_back(track_counts.alive); }; + constexpr size_type min_alloc{65536}; + result.initializers.reserve(std::min(min_alloc, max_steps_)); + result.active.reserve(std::min(min_alloc, max_steps_)); + result.alive.reserve(std::min(min_alloc, max_steps_)); + if (store_step_times_) + { + result.step_times.reserve(std::min(min_alloc, max_steps_)); + } + // Abort cleanly for interrupt and user-defined signals #ifndef _WIN32 ScopedSignalHandler interrupted{SIGINT, SIGUSR2}; @@ -165,8 +174,7 @@ void Transporter::accum_action_times(MapStrDouble* result) const CELER_ASSERT(action_ptrs.size() == times.size()); for (auto i : range(action_ptrs.size())) { - auto&& label = action_ptrs[i]->label(); - (*result)[label] += times[i]; + (*result)[std::string{action_ptrs[i]->label()}] += times[i]; } } } diff --git a/src/accel/GeantSimpleCalo.cc b/src/accel/GeantSimpleCalo.cc index 7cfb46dede..b85308d347 100644 --- a/src/accel/GeantSimpleCalo.cc +++ b/src/accel/GeantSimpleCalo.cc @@ -172,7 +172,7 @@ auto GeantSimpleCalo::CalcTotalEnergyDeposition() const -> VecReal /*! * Return the key in the JSON output. */ -std::string GeantSimpleCalo::label() const +std::string_view GeantSimpleCalo::label() const { return storage_->name; } diff --git a/src/accel/GeantSimpleCalo.hh b/src/accel/GeantSimpleCalo.hh index bad1ac07f4..84ddaa756e 100644 --- a/src/accel/GeantSimpleCalo.hh +++ b/src/accel/GeantSimpleCalo.hh @@ -65,7 +65,7 @@ class GeantSimpleCalo final : public OutputInterface //! Category of data to write Category category() const final { return Category::result; } // Key for the entry inside the category - std::string label() const final; + std::string_view label() const final; // Write output to the given JSON object void output(JsonPimpl*) const final; //!@} diff --git a/src/accel/GeantStepDiagnostic.hh b/src/accel/GeantStepDiagnostic.hh index 44d50dafb8..2855ba4d6e 100644 --- a/src/accel/GeantStepDiagnostic.hh +++ b/src/accel/GeantStepDiagnostic.hh @@ -44,7 +44,7 @@ class GeantStepDiagnostic final : public OutputInterface //! Category of data to write Category category() const final { return Category::result; } //! Key for the entry inside the category. - std::string label() const final { return "g4-step-diagnostic"; } + std::string_view label() const final { return "g4-step-diagnostic"; } // Write output to the given JSON object void output(JsonPimpl*) const final; //!@} diff --git a/src/accel/LocalTransporter.cc b/src/accel/LocalTransporter.cc index 2e243619db..abdba3f3cd 100644 --- a/src/accel/LocalTransporter.cc +++ b/src/accel/LocalTransporter.cc @@ -262,8 +262,7 @@ auto LocalTransporter::GetActionTime() const -> MapStrReal CELER_ASSERT(action_ptrs.size() == time.size()); for (auto i : range(action_ptrs.size())) { - auto&& label = action_ptrs[i]->label(); - result[label] = time[i]; + result[std::string{action_ptrs[i]->label()}] = time[i]; } } return result; diff --git a/src/celeritas/em/model/BetheHeitlerModel.hh b/src/celeritas/em/model/BetheHeitlerModel.hh index 098fb54b70..ca1083e73b 100644 --- a/src/celeritas/em/model/BetheHeitlerModel.hh +++ b/src/celeritas/em/model/BetheHeitlerModel.hh @@ -52,10 +52,10 @@ class BetheHeitlerModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "conv-bethe-heitler"; } + std::string_view label() const final { return "conv-bethe-heitler"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Bethe-Heitler gamma conversion"; } diff --git a/src/celeritas/em/model/CombinedBremModel.hh b/src/celeritas/em/model/CombinedBremModel.hh index 7d27398692..ea750bccfd 100644 --- a/src/celeritas/em/model/CombinedBremModel.hh +++ b/src/celeritas/em/model/CombinedBremModel.hh @@ -67,10 +67,10 @@ class CombinedBremModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "brems-combined"; } + std::string_view label() const final { return "brems-combined"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by bremsstrahlung (combined SB/relativistic, e+/-)"; } diff --git a/src/celeritas/em/model/CoulombScatteringModel.hh b/src/celeritas/em/model/CoulombScatteringModel.hh index 1866b571ef..236001c05b 100644 --- a/src/celeritas/em/model/CoulombScatteringModel.hh +++ b/src/celeritas/em/model/CoulombScatteringModel.hh @@ -77,10 +77,10 @@ class CoulombScatteringModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "coulomb-wentzel"; } + std::string_view label() const final { return "coulomb-wentzel"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Coulomb scattering (Wentzel)"; } diff --git a/src/celeritas/em/model/EPlusGGModel.hh b/src/celeritas/em/model/EPlusGGModel.hh index 02779b6205..46524cffc8 100644 --- a/src/celeritas/em/model/EPlusGGModel.hh +++ b/src/celeritas/em/model/EPlusGGModel.hh @@ -41,10 +41,10 @@ class EPlusGGModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "annihil-2-gamma"; } + std::string_view label() const final { return "annihil-2-gamma"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by positron annihilation yielding two gammas"; } diff --git a/src/celeritas/em/model/KleinNishinaModel.hh b/src/celeritas/em/model/KleinNishinaModel.hh index 176a469c72..9baea3953c 100644 --- a/src/celeritas/em/model/KleinNishinaModel.hh +++ b/src/celeritas/em/model/KleinNishinaModel.hh @@ -39,10 +39,10 @@ class KleinNishinaModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "scat-klein-nishina"; } + std::string_view label() const final { return "scat-klein-nishina"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Compton scattering (simple Klein-Nishina)"; } diff --git a/src/celeritas/em/model/LivermorePEModel.hh b/src/celeritas/em/model/LivermorePEModel.hh index b8d0edccee..9e492bb50d 100644 --- a/src/celeritas/em/model/LivermorePEModel.hh +++ b/src/celeritas/em/model/LivermorePEModel.hh @@ -57,10 +57,10 @@ class LivermorePEModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "photoel-livermore"; } + std::string_view label() const final { return "photoel-livermore"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Livermore photoelectric effect"; } diff --git a/src/celeritas/em/model/MollerBhabhaModel.hh b/src/celeritas/em/model/MollerBhabhaModel.hh index d6615249ef..6c79c8c288 100644 --- a/src/celeritas/em/model/MollerBhabhaModel.hh +++ b/src/celeritas/em/model/MollerBhabhaModel.hh @@ -40,10 +40,10 @@ class MollerBhabhaModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "ioni-moller-bhabha"; } + std::string_view label() const final { return "ioni-moller-bhabha"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Moller+Bhabha ionization"; } diff --git a/src/celeritas/em/model/MuBremsstrahlungModel.hh b/src/celeritas/em/model/MuBremsstrahlungModel.hh index e951b1b025..b2791e3c30 100644 --- a/src/celeritas/em/model/MuBremsstrahlungModel.hh +++ b/src/celeritas/em/model/MuBremsstrahlungModel.hh @@ -51,10 +51,10 @@ class MuBremsstrahlungModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "brems-muon"; } + std::string_view label() const final { return "brems-muon"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by bremsstrahlung (muon)"; } diff --git a/src/celeritas/em/model/RayleighModel.hh b/src/celeritas/em/model/RayleighModel.hh index bc7702b420..480920f4fb 100644 --- a/src/celeritas/em/model/RayleighModel.hh +++ b/src/celeritas/em/model/RayleighModel.hh @@ -58,10 +58,10 @@ class RayleighModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "scat-rayleigh"; } + std::string_view label() const final { return "scat-rayleigh"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Rayleigh scattering"; } diff --git a/src/celeritas/em/model/RelativisticBremModel.hh b/src/celeritas/em/model/RelativisticBremModel.hh index 398eadc925..a38e462d7f 100644 --- a/src/celeritas/em/model/RelativisticBremModel.hh +++ b/src/celeritas/em/model/RelativisticBremModel.hh @@ -61,10 +61,10 @@ class RelativisticBremModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "brems-rel"; } + std::string_view label() const final { return "brems-rel"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by relativistic bremsstrahlung"; } diff --git a/src/celeritas/em/model/SeltzerBergerModel.hh b/src/celeritas/em/model/SeltzerBergerModel.hh index ca10d525b8..9222128402 100644 --- a/src/celeritas/em/model/SeltzerBergerModel.hh +++ b/src/celeritas/em/model/SeltzerBergerModel.hh @@ -81,10 +81,10 @@ class SeltzerBergerModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "brems-sb"; } + std::string_view label() const final { return "brems-sb"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by Seltzer-Berger bremsstrahlung"; } diff --git a/src/celeritas/em/process/BremsstrahlungProcess.cc b/src/celeritas/em/process/BremsstrahlungProcess.cc index 6a1079c541..6848487085 100644 --- a/src/celeritas/em/process/BremsstrahlungProcess.cc +++ b/src/celeritas/em/process/BremsstrahlungProcess.cc @@ -113,7 +113,7 @@ auto BremsstrahlungProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string BremsstrahlungProcess::label() const +std::string_view BremsstrahlungProcess::label() const { return "Bremsstrahlung"; } diff --git a/src/celeritas/em/process/BremsstrahlungProcess.hh b/src/celeritas/em/process/BremsstrahlungProcess.hh index 24374f9ebf..af2dff4604 100644 --- a/src/celeritas/em/process/BremsstrahlungProcess.hh +++ b/src/celeritas/em/process/BremsstrahlungProcess.hh @@ -69,7 +69,7 @@ class BremsstrahlungProcess : public Process bool use_integral_xs() const final { return options_.use_integral_xs; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/ComptonProcess.cc b/src/celeritas/em/process/ComptonProcess.cc index ba21387722..c754064678 100644 --- a/src/celeritas/em/process/ComptonProcess.cc +++ b/src/celeritas/em/process/ComptonProcess.cc @@ -53,7 +53,7 @@ auto ComptonProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string ComptonProcess::label() const +std::string_view ComptonProcess::label() const { return "Compton scattering"; } diff --git a/src/celeritas/em/process/ComptonProcess.hh b/src/celeritas/em/process/ComptonProcess.hh index ae2476f595..6448ef31e8 100644 --- a/src/celeritas/em/process/ComptonProcess.hh +++ b/src/celeritas/em/process/ComptonProcess.hh @@ -43,7 +43,7 @@ class ComptonProcess : public Process bool use_integral_xs() const final { return false; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/CoulombScatteringProcess.cc b/src/celeritas/em/process/CoulombScatteringProcess.cc index d6b6f6cfd3..5eac088a7e 100644 --- a/src/celeritas/em/process/CoulombScatteringProcess.cc +++ b/src/celeritas/em/process/CoulombScatteringProcess.cc @@ -62,7 +62,7 @@ auto CoulombScatteringProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string CoulombScatteringProcess::label() const +std::string_view CoulombScatteringProcess::label() const { return "Coulomb scattering"; } diff --git a/src/celeritas/em/process/CoulombScatteringProcess.hh b/src/celeritas/em/process/CoulombScatteringProcess.hh index a09c2e04c0..a2f8db943d 100644 --- a/src/celeritas/em/process/CoulombScatteringProcess.hh +++ b/src/celeritas/em/process/CoulombScatteringProcess.hh @@ -51,7 +51,7 @@ class CoulombScatteringProcess : public Process bool use_integral_xs() const final; // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/EIonizationProcess.cc b/src/celeritas/em/process/EIonizationProcess.cc index b915520f1a..3ced1f58fa 100644 --- a/src/celeritas/em/process/EIonizationProcess.cc +++ b/src/celeritas/em/process/EIonizationProcess.cc @@ -57,7 +57,7 @@ auto EIonizationProcess::step_limits(Applicability applicability) const /*! * Name of the process. */ -std::string EIonizationProcess::label() const +std::string_view EIonizationProcess::label() const { return "Electron/positron ionization"; } diff --git a/src/celeritas/em/process/EIonizationProcess.hh b/src/celeritas/em/process/EIonizationProcess.hh index b8fca5282a..8a26e2ca3d 100644 --- a/src/celeritas/em/process/EIonizationProcess.hh +++ b/src/celeritas/em/process/EIonizationProcess.hh @@ -52,7 +52,7 @@ class EIonizationProcess : public Process bool use_integral_xs() const final { return options_.use_integral_xs; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/EPlusAnnihilationProcess.cc b/src/celeritas/em/process/EPlusAnnihilationProcess.cc index 16f387fc9d..c506703acd 100644 --- a/src/celeritas/em/process/EPlusAnnihilationProcess.cc +++ b/src/celeritas/em/process/EPlusAnnihilationProcess.cc @@ -63,7 +63,7 @@ auto EPlusAnnihilationProcess::step_limits(Applicability range) const /*! * Name of the process. */ -std::string EPlusAnnihilationProcess::label() const +std::string_view EPlusAnnihilationProcess::label() const { return "Positron annihiliation"; } diff --git a/src/celeritas/em/process/EPlusAnnihilationProcess.hh b/src/celeritas/em/process/EPlusAnnihilationProcess.hh index cc79d6974f..4894175c81 100644 --- a/src/celeritas/em/process/EPlusAnnihilationProcess.hh +++ b/src/celeritas/em/process/EPlusAnnihilationProcess.hh @@ -49,7 +49,7 @@ class EPlusAnnihilationProcess final : public Process bool use_integral_xs() const final { return options_.use_integral_xs; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/GammaConversionProcess.cc b/src/celeritas/em/process/GammaConversionProcess.cc index 89674eac6b..1a5a2fee22 100644 --- a/src/celeritas/em/process/GammaConversionProcess.cc +++ b/src/celeritas/em/process/GammaConversionProcess.cc @@ -59,7 +59,7 @@ auto GammaConversionProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string GammaConversionProcess::label() const +std::string_view GammaConversionProcess::label() const { return "Photon annihiliation"; } diff --git a/src/celeritas/em/process/GammaConversionProcess.hh b/src/celeritas/em/process/GammaConversionProcess.hh index 49cea2bceb..916819cef9 100644 --- a/src/celeritas/em/process/GammaConversionProcess.hh +++ b/src/celeritas/em/process/GammaConversionProcess.hh @@ -51,7 +51,7 @@ class GammaConversionProcess : public Process bool use_integral_xs() const final { return false; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/PhotoelectricProcess.cc b/src/celeritas/em/process/PhotoelectricProcess.cc index d85010c213..aeba333792 100644 --- a/src/celeritas/em/process/PhotoelectricProcess.cc +++ b/src/celeritas/em/process/PhotoelectricProcess.cc @@ -62,7 +62,7 @@ auto PhotoelectricProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string PhotoelectricProcess::label() const +std::string_view PhotoelectricProcess::label() const { return "Photoelectric effect"; } diff --git a/src/celeritas/em/process/PhotoelectricProcess.hh b/src/celeritas/em/process/PhotoelectricProcess.hh index b131baa1c0..d7ef6455d7 100644 --- a/src/celeritas/em/process/PhotoelectricProcess.hh +++ b/src/celeritas/em/process/PhotoelectricProcess.hh @@ -53,7 +53,7 @@ class PhotoelectricProcess : public Process bool use_integral_xs() const final { return false; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/em/process/RayleighProcess.cc b/src/celeritas/em/process/RayleighProcess.cc index c86140fd51..cda381101d 100644 --- a/src/celeritas/em/process/RayleighProcess.cc +++ b/src/celeritas/em/process/RayleighProcess.cc @@ -57,7 +57,7 @@ auto RayleighProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string RayleighProcess::label() const +std::string_view RayleighProcess::label() const { return "Rayleigh scattering"; } diff --git a/src/celeritas/em/process/RayleighProcess.hh b/src/celeritas/em/process/RayleighProcess.hh index ff35be96e8..945f0c6567 100644 --- a/src/celeritas/em/process/RayleighProcess.hh +++ b/src/celeritas/em/process/RayleighProcess.hh @@ -47,7 +47,7 @@ class RayleighProcess : public Process bool use_integral_xs() const final { return false; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/global/ActionInterface.hh b/src/celeritas/global/ActionInterface.hh index e30d4c9097..bd2b20b1be 100644 --- a/src/celeritas/global/ActionInterface.hh +++ b/src/celeritas/global/ActionInterface.hh @@ -8,6 +8,7 @@ #pragma once #include +#include #include "celeritas/Types.hh" // IWYU pragma: export @@ -58,10 +59,10 @@ class ActionInterface virtual ActionId action_id() const = 0; //! Short unique label of the action - virtual std::string label() const = 0; + virtual std::string_view label() const = 0; //! Description of the action - virtual std::string description() const = 0; + virtual std::string_view description() const = 0; protected: //!@{ @@ -194,10 +195,10 @@ class ConcreteAction : public virtual ActionInterface ActionId action_id() const final { return id_; } //! Short label - std::string label() const final { return label_; } + std::string_view label() const final { return label_; } //! Descriptive label - std::string description() const final { return description_; } + std::string_view description() const final { return description_; } private: ActionId id_; diff --git a/src/celeritas/global/ActionLauncher.device.hh b/src/celeritas/global/ActionLauncher.device.hh index e30ee91da7..5171cdff49 100644 --- a/src/celeritas/global/ActionLauncher.device.hh +++ b/src/celeritas/global/ActionLauncher.device.hh @@ -75,7 +75,7 @@ class ActionLauncher //! Create a launcher with a string extension ActionLauncher(ExplicitActionInterface const& action, std::string_view ext) - : ActionLauncher{action.label() + "-" + std::string(ext)} + : ActionLauncher{std::string(action.label()) + "-" + std::string(ext)} { } diff --git a/src/celeritas/global/ActionRegistry.cc b/src/celeritas/global/ActionRegistry.cc index ca01fac3a3..f932337322 100644 --- a/src/celeritas/global/ActionRegistry.cc +++ b/src/celeritas/global/ActionRegistry.cc @@ -49,7 +49,7 @@ void ActionRegistry::insert_const_impl(SPConstAction&& action) */ void ActionRegistry::insert_impl(SPConstAction&& action) { - auto label = action->label(); + auto label = std::string{action->label()}; CELER_VALIDATE(!label.empty(), << "action label is empty"); auto id = action->action_id(); diff --git a/src/celeritas/global/ActionRegistryOutput.hh b/src/celeritas/global/ActionRegistryOutput.hh index b5fcbe1627..465c2cec14 100644 --- a/src/celeritas/global/ActionRegistryOutput.hh +++ b/src/celeritas/global/ActionRegistryOutput.hh @@ -34,7 +34,7 @@ class ActionRegistryOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "actions"; } + std::string_view label() const final { return "actions"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/celeritas/global/KernelContextException.cc b/src/celeritas/global/KernelContextException.cc index cb539bf5ab..f715f9bb2b 100644 --- a/src/celeritas/global/KernelContextException.cc +++ b/src/celeritas/global/KernelContextException.cc @@ -46,8 +46,8 @@ KernelContextException::KernelContextException( HostCRef const& params, HostRef const& states, ThreadId thread, - std::string&& label) - : thread_(thread), label_(std::move(label)) + std::string_view label) + : thread_(thread), label_{label} { try { diff --git a/src/celeritas/global/KernelContextException.hh b/src/celeritas/global/KernelContextException.hh index a3db0cfdbe..df3c4806c3 100644 --- a/src/celeritas/global/KernelContextException.hh +++ b/src/celeritas/global/KernelContextException.hh @@ -7,7 +7,10 @@ //---------------------------------------------------------------------------// #pragma once +#include + #include "corecel/Assert.hh" +#include "corecel/io/JsonPimpl.hh" #include "celeritas/Quantities.hh" #include "celeritas/Types.hh" @@ -47,7 +50,7 @@ class KernelContextException : public RichContextException KernelContextException(HostCRef const& params, HostRef const& states, ThreadId tid, - std::string&& label); + std::string_view label); // This class type char const* type() const final; diff --git a/src/celeritas/global/alongstep/AlongStepGeneralLinearAction.hh b/src/celeritas/global/alongstep/AlongStepGeneralLinearAction.hh index 76daea6100..ee5bdc19ec 100644 --- a/src/celeritas/global/alongstep/AlongStepGeneralLinearAction.hh +++ b/src/celeritas/global/alongstep/AlongStepGeneralLinearAction.hh @@ -68,10 +68,13 @@ class AlongStepGeneralLinearAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the along-step kernel - std::string label() const final { return "along-step-general-linear"; } + std::string_view label() const final + { + return "along-step-general-linear"; + } //! Short description of the action - std::string description() const final + std::string_view description() const final { return "apply along-step for particles with no field"; } diff --git a/src/celeritas/global/alongstep/AlongStepNeutralAction.hh b/src/celeritas/global/alongstep/AlongStepNeutralAction.hh index 7b1f3b9490..50f6bdf113 100644 --- a/src/celeritas/global/alongstep/AlongStepNeutralAction.hh +++ b/src/celeritas/global/alongstep/AlongStepNeutralAction.hh @@ -38,10 +38,10 @@ class AlongStepNeutralAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the along-step kernel - std::string label() const final { return "along-step-neutral"; } + std::string_view label() const final { return "along-step-neutral"; } //! Short description of the action - std::string description() const final + std::string_view description() const final { return "apply along-step for neutral particles"; } diff --git a/src/celeritas/global/alongstep/AlongStepRZMapFieldMscAction.hh b/src/celeritas/global/alongstep/AlongStepRZMapFieldMscAction.hh index 607bf76646..dc137148d3 100644 --- a/src/celeritas/global/alongstep/AlongStepRZMapFieldMscAction.hh +++ b/src/celeritas/global/alongstep/AlongStepRZMapFieldMscAction.hh @@ -67,10 +67,10 @@ class AlongStepRZMapFieldMscAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the interaction kernel - std::string label() const final { return "along-step-rzmap-msc"; } + std::string_view label() const final { return "along-step-rzmap-msc"; } //! Short description of the action - std::string description() const final + std::string_view description() const final { return "apply along-step in a R-Z map field with Urban MSC"; } diff --git a/src/celeritas/global/alongstep/AlongStepUniformMscAction.hh b/src/celeritas/global/alongstep/AlongStepUniformMscAction.hh index b4b5af70cf..fe622c7638 100644 --- a/src/celeritas/global/alongstep/AlongStepUniformMscAction.hh +++ b/src/celeritas/global/alongstep/AlongStepUniformMscAction.hh @@ -67,10 +67,10 @@ class AlongStepUniformMscAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the interaction kernel - std::string label() const final { return "along-step-uniform-msc"; } + std::string_view label() const final { return "along-step-uniform-msc"; } //! Short description of the action - std::string description() const final + std::string_view description() const final { return "apply along-step in a uniform field with Urban MSC"; } diff --git a/src/celeritas/mat/MaterialParamsOutput.hh b/src/celeritas/mat/MaterialParamsOutput.hh index 6d109c49ef..cb95c7135c 100644 --- a/src/celeritas/mat/MaterialParamsOutput.hh +++ b/src/celeritas/mat/MaterialParamsOutput.hh @@ -34,7 +34,7 @@ class MaterialParamsOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "material"; } + std::string_view label() const final { return "material"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/celeritas/neutron/model/ChipsNeutronElasticModel.hh b/src/celeritas/neutron/model/ChipsNeutronElasticModel.hh index 1c1c804362..bdf19102bd 100644 --- a/src/celeritas/neutron/model/ChipsNeutronElasticModel.hh +++ b/src/celeritas/neutron/model/ChipsNeutronElasticModel.hh @@ -61,10 +61,10 @@ class ChipsNeutronElasticModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "neutron-elastic-chips"; } + std::string_view label() const final { return "neutron-elastic-chips"; } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by neutron elastic scattering (CHIPS)"; } diff --git a/src/celeritas/neutron/model/NeutronInelasticModel.hh b/src/celeritas/neutron/model/NeutronInelasticModel.hh index bfe247d4e5..fd9ab43858 100644 --- a/src/celeritas/neutron/model/NeutronInelasticModel.hh +++ b/src/celeritas/neutron/model/NeutronInelasticModel.hh @@ -61,10 +61,13 @@ class NeutronInelasticModel final : public Model ActionId action_id() const final; //! Short name for the interaction kernel - std::string label() const final { return "neutron-inelastic-bertini"; } + std::string_view label() const final + { + return "neutron-inelastic-bertini"; + } //! Short description of the post-step action - std::string description() const final + std::string_view description() const final { return "interact by neutron inelastic (Bertini)"; } diff --git a/src/celeritas/neutron/process/NeutronElasticProcess.cc b/src/celeritas/neutron/process/NeutronElasticProcess.cc index 6455062684..09f4b48817 100644 --- a/src/celeritas/neutron/process/NeutronElasticProcess.cc +++ b/src/celeritas/neutron/process/NeutronElasticProcess.cc @@ -61,7 +61,7 @@ auto NeutronElasticProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string NeutronElasticProcess::label() const +std::string_view NeutronElasticProcess::label() const { return "Neutron elastic"; } diff --git a/src/celeritas/neutron/process/NeutronElasticProcess.hh b/src/celeritas/neutron/process/NeutronElasticProcess.hh index 8f9a83e292..fb16f0a24a 100644 --- a/src/celeritas/neutron/process/NeutronElasticProcess.hh +++ b/src/celeritas/neutron/process/NeutronElasticProcess.hh @@ -50,7 +50,7 @@ class NeutronElasticProcess : public Process bool use_integral_xs() const final { return false; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/neutron/process/NeutronInelasticProcess.cc b/src/celeritas/neutron/process/NeutronInelasticProcess.cc index 5d5264af24..f687812202 100644 --- a/src/celeritas/neutron/process/NeutronInelasticProcess.cc +++ b/src/celeritas/neutron/process/NeutronInelasticProcess.cc @@ -61,7 +61,7 @@ auto NeutronInelasticProcess::step_limits(Applicability applic) const /*! * Name of the process. */ -std::string NeutronInelasticProcess::label() const +std::string_view NeutronInelasticProcess::label() const { return "Neutron inelastic"; } diff --git a/src/celeritas/neutron/process/NeutronInelasticProcess.hh b/src/celeritas/neutron/process/NeutronInelasticProcess.hh index 7f6d2f0b99..b386b5ecb1 100644 --- a/src/celeritas/neutron/process/NeutronInelasticProcess.hh +++ b/src/celeritas/neutron/process/NeutronInelasticProcess.hh @@ -50,7 +50,7 @@ class NeutronInelasticProcess : public Process bool use_integral_xs() const final { return false; } // Name of the process - std::string label() const final; + std::string_view label() const final; private: SPConstParticles particles_; diff --git a/src/celeritas/optical/detail/PreGenAction.cc b/src/celeritas/optical/detail/PreGenAction.cc index af060d870f..59338d4dc6 100644 --- a/src/celeritas/optical/detail/PreGenAction.cc +++ b/src/celeritas/optical/detail/PreGenAction.cc @@ -50,7 +50,7 @@ PreGenAction::PreGenAction(ActionId id, /*! * Descriptive name of the action. */ -std::string PreGenAction::description() const +std::string_view PreGenAction::description() const { return "generate Cerenkov and scintillation optical distribution data"; } diff --git a/src/celeritas/optical/detail/PreGenAction.hh b/src/celeritas/optical/detail/PreGenAction.hh index 02605fb300..7350dfe0ba 100644 --- a/src/celeritas/optical/detail/PreGenAction.hh +++ b/src/celeritas/optical/detail/PreGenAction.hh @@ -66,10 +66,13 @@ class PreGenAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "optical-pre-generator-post"; } + std::string_view label() const final + { + return "optical-pre-generator-post"; + } // Name of the action (for user output) - std::string description() const final; + std::string_view description() const final; //! Dependency ordering of the action ActionOrder order() const final { return ActionOrder::post_post; } diff --git a/src/celeritas/optical/detail/PreGenGatherAction.cc b/src/celeritas/optical/detail/PreGenGatherAction.cc index da332bc24e..13766bdb69 100644 --- a/src/celeritas/optical/detail/PreGenGatherAction.cc +++ b/src/celeritas/optical/detail/PreGenGatherAction.cc @@ -38,7 +38,7 @@ PreGenGatherAction::PreGenGatherAction(ActionId id, SPGenStorage storage) /*! * Descriptive name of the action. */ -std::string PreGenGatherAction::description() const +std::string_view PreGenGatherAction::description() const { return "gather pre-step data to generate optical distributions"; } diff --git a/src/celeritas/optical/detail/PreGenGatherAction.hh b/src/celeritas/optical/detail/PreGenGatherAction.hh index 56ea88f3b7..33ea8bbb9f 100644 --- a/src/celeritas/optical/detail/PreGenGatherAction.hh +++ b/src/celeritas/optical/detail/PreGenGatherAction.hh @@ -45,10 +45,13 @@ class PreGenGatherAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "optical-pre-generator-pre"; } + std::string_view label() const final + { + return "optical-pre-generator-pre"; + } // Name of the action (for user output) - std::string description() const final; + std::string_view description() const final; //! Dependency ordering of the action ActionOrder order() const final { return ActionOrder::pre; } diff --git a/src/celeritas/phys/ParticleParamsOutput.hh b/src/celeritas/phys/ParticleParamsOutput.hh index 8fbc2880c1..671611cc99 100644 --- a/src/celeritas/phys/ParticleParamsOutput.hh +++ b/src/celeritas/phys/ParticleParamsOutput.hh @@ -34,7 +34,7 @@ class ParticleParamsOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "particle"; } + std::string_view label() const final { return "particle"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/celeritas/phys/PhysicsParamsOutput.hh b/src/celeritas/phys/PhysicsParamsOutput.hh index 902ef2123c..8489c66829 100644 --- a/src/celeritas/phys/PhysicsParamsOutput.hh +++ b/src/celeritas/phys/PhysicsParamsOutput.hh @@ -33,7 +33,7 @@ class PhysicsParamsOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "physics"; } + std::string_view label() const final { return "physics"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/celeritas/phys/Process.hh b/src/celeritas/phys/Process.hh index ef7fab5d6c..fd6bee707a 100644 --- a/src/celeritas/phys/Process.hh +++ b/src/celeritas/phys/Process.hh @@ -67,7 +67,7 @@ class Process virtual bool use_integral_xs() const = 0; //! Name of the process - virtual std::string label() const = 0; + virtual std::string_view label() const = 0; protected: //!@{ diff --git a/src/celeritas/track/ExtendFromPrimariesAction.hh b/src/celeritas/track/ExtendFromPrimariesAction.hh index c9fc68c3f4..82d52e7dc9 100644 --- a/src/celeritas/track/ExtendFromPrimariesAction.hh +++ b/src/celeritas/track/ExtendFromPrimariesAction.hh @@ -38,10 +38,10 @@ class ExtendFromPrimariesAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "extend-from-primaries"; } + std::string_view label() const final { return "extend-from-primaries"; } //! Description of the action for user interaction - std::string description() const final + std::string_view description() const final { return "create track initializers from primaries"; } diff --git a/src/celeritas/track/ExtendFromSecondariesAction.cc b/src/celeritas/track/ExtendFromSecondariesAction.cc index 13e8ee1343..901df3160c 100644 --- a/src/celeritas/track/ExtendFromSecondariesAction.cc +++ b/src/celeritas/track/ExtendFromSecondariesAction.cc @@ -23,7 +23,7 @@ namespace celeritas /*! * Get a long description of the action. */ -std::string ExtendFromSecondariesAction::description() const +std::string_view ExtendFromSecondariesAction::description() const { return "create track initializers from secondaries"; } diff --git a/src/celeritas/track/ExtendFromSecondariesAction.hh b/src/celeritas/track/ExtendFromSecondariesAction.hh index 42692ccfd0..5d61b18d9b 100644 --- a/src/celeritas/track/ExtendFromSecondariesAction.hh +++ b/src/celeritas/track/ExtendFromSecondariesAction.hh @@ -87,9 +87,9 @@ class ExtendFromSecondariesAction final : public ExplicitCoreActionInterface, //! ID of the action ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "extend-from-secondaries"; } + std::string_view label() const final { return "extend-from-secondaries"; } // Description of the action for user interaction - std::string description() const final; + std::string_view description() const final; //! Dependency ordering of the action ActionOrder order() const final { return ActionOrder::end; } //!@} diff --git a/src/celeritas/track/InitializeTracksAction.hh b/src/celeritas/track/InitializeTracksAction.hh index 851e9ff755..02d5d87402 100644 --- a/src/celeritas/track/InitializeTracksAction.hh +++ b/src/celeritas/track/InitializeTracksAction.hh @@ -37,10 +37,13 @@ class InitializeTracksAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "initialize-tracks"; } + std::string_view label() const final { return "initialize-tracks"; } //! Description of the action for user interaction - std::string description() const final { return "initialize track states"; } + std::string_view description() const final + { + return "initialize track states"; + } //! Dependency ordering of the action ActionOrder order() const final { return ActionOrder::start; } diff --git a/src/celeritas/track/SortTracksAction.cc b/src/celeritas/track/SortTracksAction.cc index 43111dea41..7a4062feba 100644 --- a/src/celeritas/track/SortTracksAction.cc +++ b/src/celeritas/track/SortTracksAction.cc @@ -90,7 +90,7 @@ SortTracksAction::SortTracksAction(ActionId id, TrackOrder track_order) /*! * Short name for the action */ -std::string SortTracksAction::label() const +std::string_view SortTracksAction::label() const { switch (track_order_) { diff --git a/src/celeritas/track/SortTracksAction.hh b/src/celeritas/track/SortTracksAction.hh index 0bf675dd7f..a95a9e34e4 100644 --- a/src/celeritas/track/SortTracksAction.hh +++ b/src/celeritas/track/SortTracksAction.hh @@ -52,10 +52,10 @@ class SortTracksAction final : public ExplicitCoreActionInterface, ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final; + std::string_view label() const final; //! Description of the action for user interaction - std::string description() const final { return "sort tracks states"; } + std::string_view description() const final { return "sort tracks states"; } //! Dependency ordering of the action ActionOrder order() const final { return action_order_; } diff --git a/src/celeritas/user/ActionDiagnostic.cc b/src/celeritas/user/ActionDiagnostic.cc index 3b3ef0a8d3..44c6c8e40b 100644 --- a/src/celeritas/user/ActionDiagnostic.cc +++ b/src/celeritas/user/ActionDiagnostic.cc @@ -95,7 +95,7 @@ void ActionDiagnostic::execute(CoreParams const& params, /*! * Get a long description of the action. */ -std::string ActionDiagnostic::description() const +std::string_view ActionDiagnostic::description() const { return "accumulate post-step action counters"; } diff --git a/src/celeritas/user/ActionDiagnostic.hh b/src/celeritas/user/ActionDiagnostic.hh index b002f57832..0d8d52a947 100644 --- a/src/celeritas/user/ActionDiagnostic.hh +++ b/src/celeritas/user/ActionDiagnostic.hh @@ -61,9 +61,9 @@ class ActionDiagnostic final : public ExplicitCoreActionInterface, //! ID of the action ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "action-diagnostic"; } + std::string_view label() const final { return "action-diagnostic"; } // Description of the action for user interaction - std::string description() const final; + std::string_view description() const final; //! Dependency ordering of the action ActionOrder order() const final { return ActionOrder::post; } //!@} diff --git a/src/celeritas/user/SimpleCalo.hh b/src/celeritas/user/SimpleCalo.hh index 27b08021ed..2e7ea0a04c 100644 --- a/src/celeritas/user/SimpleCalo.hh +++ b/src/celeritas/user/SimpleCalo.hh @@ -77,7 +77,7 @@ class SimpleCalo final : public StepInterface, public OutputInterface // Category of data to write Category category() const final { return Category::result; } // Key for the entry inside the category. - std::string label() const final { return output_label_; } + std::string_view label() const final { return output_label_; } // Write output to the given JSON object void output(JsonPimpl*) const final; //!@} diff --git a/src/celeritas/user/StepDiagnostic.cc b/src/celeritas/user/StepDiagnostic.cc index 59d3ac1b36..4ef477515a 100644 --- a/src/celeritas/user/StepDiagnostic.cc +++ b/src/celeritas/user/StepDiagnostic.cc @@ -89,7 +89,7 @@ void StepDiagnostic::execute(CoreParams const&, CoreStateDevice&) const /*! * Get a long description of the action. */ -std::string StepDiagnostic::description() const +std::string_view StepDiagnostic::description() const { return "accumulate total step counters"; } diff --git a/src/celeritas/user/StepDiagnostic.hh b/src/celeritas/user/StepDiagnostic.hh index 05a0ccc062..64ea8ce16d 100644 --- a/src/celeritas/user/StepDiagnostic.hh +++ b/src/celeritas/user/StepDiagnostic.hh @@ -55,9 +55,9 @@ class StepDiagnostic final : public ExplicitCoreActionInterface, //! ID of the action ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final { return "step-diagnostic"; } + std::string_view label() const final { return "step-diagnostic"; } // Description of the action for user interaction - std::string description() const final; + std::string_view description() const final; //! Dependency ordering of the action ActionOrder order() const final { return ActionOrder::post_post; } //!@} diff --git a/src/celeritas/user/detail/StepGatherAction.cc b/src/celeritas/user/detail/StepGatherAction.cc index fb65426285..22b04a05eb 100644 --- a/src/celeritas/user/detail/StepGatherAction.cc +++ b/src/celeritas/user/detail/StepGatherAction.cc @@ -40,19 +40,11 @@ StepGatherAction

::StepGatherAction(ActionId id, CELER_EXPECT(id_); CELER_EXPECT(!callbacks_.empty() || P == StepPoint::pre); CELER_EXPECT(storage_); -} -//---------------------------------------------------------------------------// -/*! - * Descriptive name of the action. - */ -template -std::string StepGatherAction

::description() const -{ - std::string result = "gather "; - result += P == StepPoint::pre ? "pre" : "post"; - result += "-step steps/hits"; - return result; + description_ = "gather "; + description_ += (P == StepPoint::pre ? "pre" : "post"); + description_ += "-step steps/hits"; + CELER_ENSURE(!description_.empty()); } //---------------------------------------------------------------------------// diff --git a/src/celeritas/user/detail/StepGatherAction.hh b/src/celeritas/user/detail/StepGatherAction.hh index 26386bf6ad..05fa44febd 100644 --- a/src/celeritas/user/detail/StepGatherAction.hh +++ b/src/celeritas/user/detail/StepGatherAction.hh @@ -57,15 +57,15 @@ class StepGatherAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the action - std::string label() const final + std::string_view label() const final { return P == StepPoint::pre ? "step-gather-pre" : P == StepPoint::post ? "step-gather-post" - : ""; + : std::string_view{}; } // Name of the action (for user output) - std::string description() const final; + std::string_view description() const final { return description_; } //! Dependency ordering of the action ActionOrder order() const final @@ -81,6 +81,7 @@ class StepGatherAction final : public ExplicitCoreActionInterface ActionId id_; SPStepStorage storage_; VecInterface callbacks_; + std::string description_; }; //---------------------------------------------------------------------------// diff --git a/src/corecel/io/BuildOutput.hh b/src/corecel/io/BuildOutput.hh index 56eb0e4231..7f9fd24d21 100644 --- a/src/corecel/io/BuildOutput.hh +++ b/src/corecel/io/BuildOutput.hh @@ -22,7 +22,7 @@ class BuildOutput final : public OutputInterface Category category() const final { return Category::system; }; //! Key for the entry inside the category. - std::string label() const final { return "build"; } + std::string_view label() const final { return "build"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/corecel/io/ExceptionOutput.hh b/src/corecel/io/ExceptionOutput.hh index eea6f2f8b1..7f27ed0448 100644 --- a/src/corecel/io/ExceptionOutput.hh +++ b/src/corecel/io/ExceptionOutput.hh @@ -46,7 +46,7 @@ class ExceptionOutput final : public OutputInterface Category category() const final { return Category::result; } // Key for the entry inside the category. - std::string label() const final { return "exception"; } + std::string_view label() const final { return "exception"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/corecel/io/OutputInterface.hh b/src/corecel/io/OutputInterface.hh index b7c19ed33a..02b354fd09 100644 --- a/src/corecel/io/OutputInterface.hh +++ b/src/corecel/io/OutputInterface.hh @@ -9,6 +9,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export namespace celeritas { @@ -42,7 +43,7 @@ class OutputInterface virtual Category category() const = 0; //! Key for the entry inside the category. - virtual std::string label() const = 0; + virtual std::string_view label() const = 0; // Write output to the given JSON object virtual void output(JsonPimpl*) const = 0; diff --git a/src/corecel/io/OutputInterfaceAdapter.hh b/src/corecel/io/OutputInterfaceAdapter.hh index 7ff9a6b0fe..90af5e0fad 100644 --- a/src/corecel/io/OutputInterfaceAdapter.hh +++ b/src/corecel/io/OutputInterfaceAdapter.hh @@ -51,7 +51,7 @@ class OutputInterfaceAdapter final : public OutputInterface Category category() const final { return cat_; } //! Label of the entry inside the category. - std::string label() const final { return label_; } + std::string_view label() const final { return label_; } // Write output to the given JSON object void output(JsonPimpl* jp) const final; diff --git a/src/corecel/io/OutputRegistry.cc b/src/corecel/io/OutputRegistry.cc index b4954b63b2..73adca4d05 100644 --- a/src/corecel/io/OutputRegistry.cc +++ b/src/corecel/io/OutputRegistry.cc @@ -41,7 +41,7 @@ void OutputRegistry::insert(SPConstInterface interface) Category cat = interface->category(); auto [prev, inserted] - = interfaces_[cat].insert({std::move(label), std::move(interface)}); + = interfaces_[cat].insert({std::string{label}, std::move(interface)}); CELER_VALIDATE(inserted, << "duplicate output entry '" << prev->first << "' for category '" << to_cstring(cat) << "'"); diff --git a/src/corecel/sys/ScopedProfiling.cuda.cc b/src/corecel/sys/ScopedProfiling.cuda.cc index 65d778e1fe..9c23f2e6fe 100644 --- a/src/corecel/sys/ScopedProfiling.cuda.cc +++ b/src/corecel/sys/ScopedProfiling.cuda.cc @@ -37,15 +37,16 @@ nvtxDomainHandle_t domain_handle() * * Insert it if it doesn't already exist. */ -nvtxStringHandle_t message_handle_for(std::string const& message) +nvtxStringHandle_t message_handle_for(std::string_view message) { static std::unordered_map registry; static std::shared_mutex mutex; + std::string temp_message{message}; { std::shared_lock lock(mutex); - if (auto message_handle = registry.find(message); + if (auto message_handle = registry.find(temp_message); message_handle != registry.end()) { return message_handle->second; @@ -53,15 +54,15 @@ nvtxStringHandle_t message_handle_for(std::string const& message) } // We did not find the handle; try to insert it - auto [iter, inserted] = [&message] { + auto [iter, inserted] = [&temp_message] { std::unique_lock lock(mutex); - return registry.insert({message, {}}); + return registry.insert({std::move(temp_message), {}}); }(); if (inserted) { // Register the domain iter->second - = nvtxDomainRegisterStringA(domain_handle(), message.c_str()); + = nvtxDomainRegisterStringA(domain_handle(), iter->first.c_str()); } return iter->second; } diff --git a/src/corecel/sys/ScopedProfiling.hh b/src/corecel/sys/ScopedProfiling.hh index 8c13e678c8..5237c4dabd 100644 --- a/src/corecel/sys/ScopedProfiling.hh +++ b/src/corecel/sys/ScopedProfiling.hh @@ -21,12 +21,12 @@ namespace celeritas */ struct ScopedProfilingInput { - std::string name; //!< Name of the range + std::string_view name; //!< Name of the range uint32_t color{}; //!< ARGB int32_t payload{}; //!< User data uint32_t category{}; //!< Category, used to group ranges together - ScopedProfilingInput(std::string const& name) : name{name} {} + ScopedProfilingInput(std::string_view n) : name{n} {} }; //---------------------------------------------------------------------------// @@ -68,7 +68,7 @@ class ScopedProfiling // Activate profiling with options explicit inline ScopedProfiling(Input const& input); // Activate profiling with just a name - explicit inline ScopedProfiling(std::string const& name); + explicit inline ScopedProfiling(std::string_view name); // Deactivate profiling inline ~ScopedProfiling(); @@ -104,7 +104,7 @@ ScopedProfiling::ScopedProfiling(Input const& input) /*! * Activate device profiling with just a name. */ -ScopedProfiling::ScopedProfiling(std::string const& name) +ScopedProfiling::ScopedProfiling(std::string_view name) : ScopedProfiling{Input{name}} { } diff --git a/src/corecel/sys/ScopedProfiling.hip.cc b/src/corecel/sys/ScopedProfiling.hip.cc index 50d490ff3b..7871db6841 100644 --- a/src/corecel/sys/ScopedProfiling.hip.cc +++ b/src/corecel/sys/ScopedProfiling.hip.cc @@ -8,6 +8,8 @@ //---------------------------------------------------------------------------// #include "ScopedProfiling.hh" +#include + #include "celeritas_sys_config.h" #include "corecel/io/Logger.hh" @@ -56,7 +58,8 @@ void ScopedProfiling::activate(Input const& input) noexcept { int result = 0; #if CELERITAS_HAVE_ROCTX - result = roctxRangePush(input.name.c_str()); + std::string temp_name{input.name}; + result = roctxRangePush(temp_name.c_str()); #endif if (result < 0) { diff --git a/src/geocel/GeoParamsOutput.hh b/src/geocel/GeoParamsOutput.hh index 1df13240ea..4a59bfef76 100644 --- a/src/geocel/GeoParamsOutput.hh +++ b/src/geocel/GeoParamsOutput.hh @@ -36,7 +36,7 @@ class GeoParamsOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "geometry"; } + std::string_view label() const final { return "geometry"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/geocel/vg/VecgeomParamsOutput.hh b/src/geocel/vg/VecgeomParamsOutput.hh index 23cd14ab57..4f77fc4f3b 100644 --- a/src/geocel/vg/VecgeomParamsOutput.hh +++ b/src/geocel/vg/VecgeomParamsOutput.hh @@ -38,7 +38,7 @@ class VecgeomParamsOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "vecgeom"; } + std::string_view label() const final { return "vecgeom"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/src/orange/OrangeParamsOutput.hh b/src/orange/OrangeParamsOutput.hh index 39081ef1d8..d45874cf4d 100644 --- a/src/orange/OrangeParamsOutput.hh +++ b/src/orange/OrangeParamsOutput.hh @@ -38,7 +38,7 @@ class OrangeParamsOutput final : public OutputInterface Category category() const final { return Category::internal; } //! Name of the entry inside the category. - std::string label() const final { return "orange"; } + std::string_view label() const final { return "orange"; } // Write output to the given JSON object void output(JsonPimpl*) const final; diff --git a/test/celeritas/global/ActionRegistry.test.cc b/test/celeritas/global/ActionRegistry.test.cc index e782398cb6..1bdd731e24 100644 --- a/test/celeritas/global/ActionRegistry.test.cc +++ b/test/celeritas/global/ActionRegistry.test.cc @@ -33,8 +33,11 @@ class MyExplicitAction final : public ExplicitCoreActionInterface, } ActionId action_id() const final { return action_id_; } - std::string label() const final { return "explicit"; } - std::string description() const final { return "explicit action test"; } + std::string_view label() const final { return "explicit"; } + std::string_view description() const final + { + return "explicit action test"; + } void begin_run(CoreParams const&, CoreStateHost&) final { diff --git a/test/celeritas/global/StepperTestBase.cc b/test/celeritas/global/StepperTestBase.cc index ce6b28c882..1a8fa030ab 100644 --- a/test/celeritas/global/StepperTestBase.cc +++ b/test/celeritas/global/StepperTestBase.cc @@ -72,7 +72,7 @@ auto StepperTestBase::check_setup() -> SetupCheckResult for (auto process_id : range(ProcessId{p.num_processes()})) { - result.processes.push_back(p.process(process_id)->label()); + result.processes.emplace_back(p.process(process_id)->label()); } // Create temporary host stepper to get action ordering @@ -80,8 +80,8 @@ auto StepperTestBase::check_setup() -> SetupCheckResult auto const& action_seq = temp_stepper.actions(); for (auto const& sp_action : action_seq.actions()) { - result.actions.push_back(sp_action->label()); - result.actions_desc.push_back(sp_action->description()); + result.actions.emplace_back(sp_action->label()); + result.actions_desc.emplace_back(sp_action->description()); } return result; diff --git a/test/celeritas/phys/MockModel.cc b/test/celeritas/phys/MockModel.cc index bf4d1dadb0..8e90968523 100644 --- a/test/celeritas/phys/MockModel.cc +++ b/test/celeritas/phys/MockModel.cc @@ -23,6 +23,15 @@ MockModel::MockModel(Input data) : data_(std::move(data)) CELER_EXPECT(data_.materials); CELER_EXPECT(data_.applic); CELER_EXPECT(data_.cb); + label_ = "mock-model-"; + label_ += std::to_string(data_.id.get() - 4); + + std::ostringstream os; + os << "MockModel(" << (data_.id.get() - 4) + << ", p=" << data_.applic.particle.get() + << ", emin=" << data_.applic.lower.value() + << ", emax=" << data_.applic.upper.value() << ")"; + description_ = std::move(os).str(); } auto MockModel::applicability() const -> SetApplicability @@ -66,21 +75,6 @@ void MockModel::execute(CoreParams const&, CoreStateDevice&) const data_.cb(this->action_id()); } -std::string MockModel::label() const -{ - return std::string("mock-model-") + std::to_string(data_.id.get() - 4); -} - -std::string MockModel::description() const -{ - std::ostringstream os; - os << "MockModel(" << (data_.id.get() - 4) - << ", p=" << data_.applic.particle.get() - << ", emin=" << data_.applic.lower.value() - << ", emax=" << data_.applic.upper.value() << ")"; - return os.str(); -} - //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas diff --git a/test/celeritas/phys/MockModel.hh b/test/celeritas/phys/MockModel.hh index 430893d1c5..fff40ca944 100644 --- a/test/celeritas/phys/MockModel.hh +++ b/test/celeritas/phys/MockModel.hh @@ -51,11 +51,13 @@ class MockModel final : public Model void execute(CoreParams const&, CoreStateHost&) const final; void execute(CoreParams const&, CoreStateDevice&) const final; ActionId action_id() const final { return data_.id; } - std::string label() const final; - std::string description() const final; + std::string_view label() const final { return label_; } + std::string_view description() const final { return description_; } private: Input data_; + std::string label_; + std::string description_; }; //---------------------------------------------------------------------------// diff --git a/test/celeritas/phys/MockProcess.cc b/test/celeritas/phys/MockProcess.cc index 0c6052c543..8b76f728c1 100644 --- a/test/celeritas/phys/MockProcess.cc +++ b/test/celeritas/phys/MockProcess.cc @@ -101,7 +101,7 @@ bool MockProcess::use_integral_xs() const } //---------------------------------------------------------------------------// -std::string MockProcess::label() const +std::string_view MockProcess::label() const { return data_.label; } diff --git a/test/celeritas/phys/MockProcess.hh b/test/celeritas/phys/MockProcess.hh index 91b706d044..6207f0bc1c 100644 --- a/test/celeritas/phys/MockProcess.hh +++ b/test/celeritas/phys/MockProcess.hh @@ -87,7 +87,7 @@ class MockProcess : public Process VecModel build_models(ActionIdIter start_id) const final; StepLimitBuilders step_limits(Applicability range) const final; bool use_integral_xs() const final; - std::string label() const final; + std::string_view label() const final; private: Input data_; diff --git a/test/celeritas/phys/Physics.test.cc b/test/celeritas/phys/Physics.test.cc index bfa9f5ec1c..735463eeb8 100644 --- a/test/celeritas/phys/Physics.test.cc +++ b/test/celeritas/phys/Physics.test.cc @@ -64,9 +64,9 @@ TEST_F(PhysicsParamsTest, accessors) std::vector process_names; for (auto process_id : range(ProcessId{p.num_processes()})) { - process_names.push_back(p.process(process_id)->label()); + process_names.emplace_back(p.process(process_id)->label()); } - std::string const expected_process_names[] + static char const* const expected_process_names[] = {"scattering", "absorption", "purrs", "hisses", "meows", "barks"}; EXPECT_VEC_EQ(expected_process_names, process_names); @@ -76,8 +76,8 @@ TEST_F(PhysicsParamsTest, accessors) for (auto model_id : range(ModelId{p.num_models()})) { Model const& m = *p.model(model_id); - model_names.push_back(m.label()); - model_desc.push_back(m.description()); + model_names.emplace_back(m.label()); + model_desc.emplace_back(m.description()); } static std::string const expected_model_names[] = { @@ -252,7 +252,7 @@ class PhysicsTrackViewHostTest : public PhysicsParamsTest ParamsHostRef params_ref; StateStore state; - std::map process_names; + std::map process_names; RandomEngine rng_; }; diff --git a/test/celeritas/track/MockInteractAction.hh b/test/celeritas/track/MockInteractAction.hh index 67763a67f8..b1d3f9b67f 100644 --- a/test/celeritas/track/MockInteractAction.hh +++ b/test/celeritas/track/MockInteractAction.hh @@ -37,8 +37,11 @@ class MockInteractAction final : public ExplicitCoreActionInterface void execute(CoreParams const&, CoreStateDevice&) const final; ActionId action_id() const final { return id_; } - std::string label() const final { return "mock-interact"; } - std::string description() const final { return "mock interact kernel"; } + std::string_view label() const final { return "mock-interact"; } + std::string_view description() const final + { + return "mock interact kernel"; + } ActionOrder order() const final { return ActionOrder::post; } // Get the number of secondaries diff --git a/test/corecel/io/OutputRegistry.test.cc b/test/corecel/io/OutputRegistry.test.cc index 46be8374ca..eb91f2bcff 100644 --- a/test/corecel/io/OutputRegistry.test.cc +++ b/test/corecel/io/OutputRegistry.test.cc @@ -33,7 +33,7 @@ class TestInterface final : public OutputInterface } Category category() const final { return cat_; } - std::string label() const final { return label_; } + std::string_view label() const final { return label_; } void output(JsonPimpl* json) const final { From d02807219d8b696b407b70e5f4caca8271e81be7 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Tue, 30 Apr 2024 14:30:51 -0500 Subject: [PATCH 29/59] Add always-on basic diagnostics to celer-sim (#1214) --- app/celer-sim/RunnerOutput.cc | 21 ++++++++++++++++++--- app/celer-sim/Transporter.cc | 26 ++++++++++++++------------ app/celer-sim/Transporter.hh | 7 +++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/app/celer-sim/RunnerOutput.cc b/app/celer-sim/RunnerOutput.cc index 369b84991c..b354cfdaf4 100644 --- a/app/celer-sim/RunnerOutput.cc +++ b/app/celer-sim/RunnerOutput.cc @@ -49,14 +49,25 @@ void RunnerOutput::output(JsonPimpl* j) const auto alive = json::array(); auto initializers = json::array(); auto num_track_slots = json::array(); + auto num_step_iterations = json::array(); + auto num_steps = json::array(); + auto num_aborted = json::array(); + auto max_queued = json::array(); auto step_times = json::array(); for (auto const& event : result_.events) { - active.push_back(event.active); - alive.push_back(event.alive); - initializers.push_back(event.initializers); + if (!event.active.empty()) + { + active.push_back(event.active); + alive.push_back(event.alive); + initializers.push_back(event.initializers); + } num_track_slots.push_back(event.num_track_slots); + num_step_iterations.push_back(event.num_step_iterations); + num_steps.push_back(event.num_steps); + num_aborted.push_back(event.num_aborted); + max_queued.push_back(event.max_queued); if (!event.step_times.empty()) { step_times.push_back(event.step_times); @@ -67,6 +78,10 @@ void RunnerOutput::output(JsonPimpl* j) const obj["alive"] = std::move(alive); obj["initializers"] = std::move(initializers); obj["num_track_slots"] = std::move(num_track_slots); + obj["num_step_iterations"] = std::move(num_step_iterations); + obj["num_steps"] = std::move(num_steps); + obj["num_aborted"] = std::move(num_aborted); + obj["max_queued"] = std::move(max_queued); obj["time"] = { {"steps", std::move(step_times)}, {"actions", result_.action_times}, diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index 30639624c2..d193409c63 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #include "Transporter.hh" +#include #include #include #include @@ -82,10 +83,17 @@ auto Transporter::operator()(SpanConstPrimary primaries) { // Initialize results TransporterResult result; - auto append_track_counts = [&result](StepperResult const& track_counts) { - result.initializers.push_back(track_counts.queued); - result.active.push_back(track_counts.active); - result.alive.push_back(track_counts.alive); + auto append_track_counts = [&](StepperResult const& track_counts) { + if (store_track_counts_) + { + result.initializers.push_back(track_counts.queued); + result.active.push_back(track_counts.active); + result.alive.push_back(track_counts.alive); + } + ++result.num_step_iterations; + result.num_steps += track_counts.active; + result.num_aborted = track_counts.alive + track_counts.queued; + result.max_queued = std::max(result.max_queued, track_counts.queued); }; constexpr size_type min_alloc{65536}; @@ -112,10 +120,7 @@ auto Transporter::operator()(SpanConstPrimary primaries) auto& step = *stepper_; // Copy primaries to device and transport the first step auto track_counts = step(primaries); - if (store_track_counts_) - { - append_track_counts(track_counts); - } + append_track_counts(track_counts); if (store_step_times_) { result.step_times.push_back(get_step_time()); @@ -141,10 +146,7 @@ auto Transporter::operator()(SpanConstPrimary primaries) get_step_time = {}; track_counts = step(); - if (store_track_counts_) - { - append_track_counts(track_counts); - } + append_track_counts(track_counts); if (store_step_times_) { result.step_times.push_back(get_step_time()); diff --git a/app/celer-sim/Transporter.hh b/app/celer-sim/Transporter.hh index f89d20a2aa..af34c93900 100644 --- a/app/celer-sim/Transporter.hh +++ b/app/celer-sim/Transporter.hh @@ -63,11 +63,18 @@ struct TransporterResult using VecReal = std::vector; using VecCount = std::vector; + // Per-step diagnostics VecCount initializers; //!< Num starting track initializers VecCount active; //!< Num tracks active at beginning of step VecCount alive; //!< Num living tracks at end of step VecReal step_times; //!< Real time per step + + // Always-on basic diagnostics size_type num_track_slots{}; //!< Number of total track slots + size_type num_step_iterations{}; //!< Total number of step iterations + size_type num_steps{}; //!< Total number of steps + size_type num_aborted{}; //!< Number of unconverged tracks + size_type max_queued{}; //!< Maximum track initializer count }; //---------------------------------------------------------------------------// From b419f6298fbb84b96b24821ea4d4c7eac551a0af Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 30 Apr 2024 16:21:14 -0400 Subject: [PATCH 30/59] Fix library macro compatibility wrappers (#1215) --- cmake/CeleritasLibrary.cmake | 21 +++++++++++++++------ scripts/cmake-presets/goldfinger.json | 1 + 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cmake/CeleritasLibrary.cmake b/cmake/CeleritasLibrary.cmake index 7aa769241f..26bff7d32c 100644 --- a/cmake/CeleritasLibrary.cmake +++ b/cmake/CeleritasLibrary.cmake @@ -13,9 +13,8 @@ Deprecated file for use by downstream code. TODO: remove in v1.0. include("${CMAKE_CURRENT_LIST_DIR}/CudaRdcUtils.cmake") -message(AUTHOR_WARNING "CeleritasLibrary has been replaced by CudaRdcUtils. Please include(CudaRdcUtils) and -replace celeritas_(add_library|add_executable|target_link_libraries) with -cuda_rdc_(...)") +message(AUTHOR_WARNING "CeleritasLibrary has been replaced by CudaRdcUtils. +Please include(CudaRdcUtils) and address the following warnings...") macro(celeritas_add_library) message(AUTHOR_WARNING "Replace with cuda_rdc_add_library") @@ -27,7 +26,17 @@ macro(celeritas_add_executable) cuda_rdc_add_executable(${ARGV}) endmacro() -macro(celeritas_link_libraries) - message(AUTHOR_WARNING "Replace with cuda_rdc_link_libraries") - cuda_rdc_link_libraries(${ARGV}) +macro(celeritas_target_link_libraries) + message(AUTHOR_WARNING "Replace with cuda_rdc_target_link_libraries") + cuda_rdc_target_link_libraries(${ARGV}) +endmacro() + +macro(celeritas_target_compile_options) + message(AUTHOR_WARNING "Replace with cuda_rdc_target_compile_options") + cuda_rdc_target_compile_options(${ARGV}) +endmacro() + +macro(celeritas_set_target_properties) + message(AUTHOR_WARNING "Replace with cuda_rdc_set_target_properties") + cuda_rdc_set_target_properties(${ARGV}) endmacro() diff --git a/scripts/cmake-presets/goldfinger.json b/scripts/cmake-presets/goldfinger.json index 9d636e30eb..c8582ba1d1 100644 --- a/scripts/cmake-presets/goldfinger.json +++ b/scripts/cmake-presets/goldfinger.json @@ -24,6 +24,7 @@ "CMAKE_CXX_STANDARD": {"type": "STRING", "value": "17"}, "CMAKE_CXX_EXTENSIONS": {"type": "BOOL", "value": "OFF"}, "CMAKE_FIND_FRAMEWORK": {"type": "STRING", "value": "LAST"}, + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install-${presetName}" "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-O2 -g -DNDEBUG -fno-inline -fno-omit-frame-pointer", "CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wno-error=deprecated -pedantic -fdiagnostics-color=always" } From 46d54b4c993c87a6197082a05e3bd5d8d4165f98 Mon Sep 17 00:00:00 2001 From: Guilherme Lima Date: Tue, 30 Apr 2024 16:38:01 -0500 Subject: [PATCH 31/59] Add ORANGE converters for G4Trap and G4GenericTrap (#1213) * Add a GenTrap converter and small fixes to trap documentation * Add a G4Trap converter based on GenTrap (Arb8) shape --- src/orange/g4org/SolidConverter.cc | 84 ++++++++++++++- src/orange/orangeinp/ConvexRegion.hh | 1 + test/orange/g4org/SolidConverter.test.cc | 130 +++++++++++++++++++++++ 3 files changed, 210 insertions(+), 5 deletions(-) diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index c78aaba5f4..db6ed1b254 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -336,8 +336,19 @@ auto SolidConverter::genericpolycone(arg_type solid_base) -> result_type auto SolidConverter::generictrap(arg_type solid_base) -> result_type { auto const& solid = dynamic_cast(solid_base); - CELER_DISCARD(solid); - CELER_NOT_IMPLEMENTED("generictrap"); + + auto const& vtx = solid.GetVertices(); + CELER_ASSERT(vtx.size() == 8); + + std::vector lower(4), upper(4); + for (auto i : range(4)) + { + lower[i] = scale_.to(vtx[i].x(), vtx[i].y()); + upper[i] = scale_.to(vtx[i + 4].x(), vtx[i + 4].y()); + } + real_type hh = scale_(solid.GetZHalfLength()); + + return make_shape(solid, hh, std::move(lower), std::move(upper)); } //---------------------------------------------------------------------------// @@ -569,12 +580,75 @@ auto SolidConverter::torus(arg_type solid_base) -> result_type } //---------------------------------------------------------------------------// -//! Convert a generic trapezoid +/*! + * Convert a trapezoid. + * + * Here is the full description of the G4Trap parameters, as named here: + * + * hz - Half Z length - distance from the origin to the bases + * hy1 - Half Y length of the base at -pDz + * hy2 - Half Y length of the base at +pDz + * hx1 - Half X length at smaller Y of the base at -pDz + * hx2 - Half X length at bigger Y of the base at -pDz + * hx3 - Half X length at smaller Y of the base at +pDz + * hx4 - Half X length at bigger y of the base at +pDz + * Theta - Polar angle of the line joining the centres of the bases at -/+hz + * Phi - Azimuthal angle of the line joining the centre of the base at -hz + * to the centre of the base at +hz + * Alph1 - Angle between the Y-axis and the centre line of the base at -hz + * Alph2 - Angle between the Y-axis and the centre line of the base at +hz + * + * Note that the numbers of x,y,z parameters in the G4Trap are related to the + * fact that the two z-faces are parallel (separated by hz) and the 4 x-wedges + * (2 in each z-face) are also parallel (separated by hy1,2). + * + * Reference: + * https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html#constructed-solid-geometry-csg-solids + */ auto SolidConverter::trap(arg_type solid_base) -> result_type { auto const& solid = dynamic_cast(solid_base); - CELER_DISCARD(solid); - CELER_NOT_IMPLEMENTED("trap"); + + real_type sin_phi{}, cos_phi{}, tan_theta{}; +#if G4VERSION_NUMBER < 1100 + // Geant4 10.7 and earlier - axis = (sinth.cosphi, sinth.sinphi, costh) + // Note: both sinth,costh >= 0 since theta is in [0, pi/2] for a G4Trap + auto axis = solid.GetSymAxis(); + auto sin_theta = std::sqrt(real_type(1.0) - axis.z() * axis.z()); + tan_theta = SoftZero{1.e-8}(axis.z()) + ? sin_theta / axis.z() + : numeric_limits::infinity(); + cos_phi = sin_theta > 0 ? axis.x() / sin_theta : 1.0; + sin_phi = sin_theta > 0 ? axis.y() / sin_theta : 1.0; +#else + // Geant4 11 and later + sincos(solid.GetPhi(), &sin_phi, &cos_phi); + tan_theta = std::tan(solid.GetTheta()); +#endif + + auto hz = scale_(solid.GetZHalfLength()); + auto hy1 = scale_(solid.GetYHalfLength1()); + auto hy2 = scale_(solid.GetYHalfLength2()); + auto hx1 = scale_(solid.GetXHalfLength1()); + auto hx2 = scale_(solid.GetXHalfLength2()); + auto hx3 = scale_(solid.GetXHalfLength3()); + auto hx4 = scale_(solid.GetXHalfLength4()); + auto dxdz_hz = tan_theta * cos_phi * hz; + auto dydz_hz = tan_theta * sin_phi * hz; + auto dxdy_hy1 = solid.GetTanAlpha1() * hy1; + auto dxdy_hy2 = solid.GetTanAlpha2() * hy2; + + std::vector lower(4), upper(4); + lower[0] = GenTrap::Real2{-dxdz_hz - dxdy_hy1 - hx1, -dydz_hz - hy1}; + lower[1] = GenTrap::Real2{-dxdz_hz - dxdy_hy1 + hx1, -dydz_hz - hy1}; + lower[2] = GenTrap::Real2{-dxdz_hz + dxdy_hy1 + hx2, -dydz_hz + hy1}; + lower[3] = GenTrap::Real2{-dxdz_hz + dxdy_hy1 - hx2, -dydz_hz + hy1}; + upper[0] = GenTrap::Real2{+dxdz_hz - dxdy_hy2 - hx3, +dydz_hz - hy2}; + upper[1] = GenTrap::Real2{+dxdz_hz - dxdy_hy2 + hx3, +dydz_hz - hy2}; + upper[2] = GenTrap::Real2{+dxdz_hz + dxdy_hy2 + hx4, +dydz_hz + hy2}; + upper[3] = GenTrap::Real2{+dxdz_hz + dxdy_hy2 - hx4, +dydz_hz + hy2}; + + return make_shape(solid, hz, lower, upper); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/ConvexRegion.hh b/src/orange/orangeinp/ConvexRegion.hh index 53922f1173..591f7b4b1a 100644 --- a/src/orange/orangeinp/ConvexRegion.hh +++ b/src/orange/orangeinp/ConvexRegion.hh @@ -209,6 +209,7 @@ class Ellipsoid final : public ConvexRegionInterface */ class GenTrap final : public ConvexRegionInterface { + public: //!@{ //! \name Type aliases using Real2 = Array; diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index 4a12df0e34..3838c0308a 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -195,6 +195,91 @@ TEST_F(SolidConverterTest, displaced) {{1, 2, 3}, {2, 2, 3}, {3, 0, 0}}); } +TEST_F(SolidConverterTest, generictrap) +{ + { + this->build_and_test( + G4GenericTrap("boxGenTrap", + 30, + {{-10, -20}, + {-10, 20}, + {10, 20}, + {10, -20}, + {-10, -20}, + {-10, 20}, + {10, 20}, + {10, -20}}), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":3.0, + "lower":[[1.0,-2.0],[1.0,2.0],[-1.0,2.0],[-1.0,-2.0]], + "upper":[[1.0,-2.0],[1.0,2.0],[-1.0,2.0],[-1.0,-2.0]]}, + "label":"boxGenTrap"})json", + {{-1, -2, -3}, {1, 2, 3}, {1, 2, 4}}); + } + + { + this->build_and_test( + G4GenericTrap("trdGenTrap", + 3, + {{-10, -20}, + {-10, 20}, + {10, 20}, + {10, -20}, + {-5, -10}, + {-5, 10}, + {5, 10}, + {5, -10}}), + R"json({"_type":"shape","interior":{"_type":"gentrap" + ,"halfheight":0.30000000000000004, + "lower":[[1.0,-2.0],[1.0,2.0],[-1.0,2.0],[-1.0,-2.0]], + "upper":[[0.5,-1.0],[0.5,1.0],[-0.5,1.0],[-0.5,-1.0]]}, + "label":"trdGenTrap"})json", + {{-1, -2, -4}, {-1, -2, -3}, {0.5, 1, 3}, {1, 1, 3}}); + } + + { + this->build_and_test( + G4GenericTrap("trap_GenTrap", + 40, + {{-9, -20}, + {-9, 20}, + {11, 20}, + {11, -20}, + {-29, -40}, + {-29, 40}, + {31, 40}, + {31, -40}}), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":4.0, + "lower":[[1.1,-2.0],[1.1,2.0],[-0.9,2.0],[-0.9,-2.0]], + "upper":[[3.1,-4.0],[3.1,4.0],[-2.9,4.0],[-2.9,-4.0]]}, + "label":"trap_GenTrap"})json", + {{-1, -2, -4 - 1.e-6}, {-1, -2, -3}, {0.5, 1, 3}, {1, 1, 3}}); + } + + { + // TODO: most generic gentrap with twisted side faces + EXPECT_THROW( + this->build_and_test( + G4GenericTrap("LArEMECInnerWheelAbsorber02", + 10.625, + {{1.55857990922689, 302.468976599716}, + {-1.73031296208306, 302.468976599716}, + {-2.53451906396442, 609.918546236458}, + {2.18738922312177, 609.918546236458}, + {-11.9586196560814, 304.204253530802}, + {-15.2556006134987, 304.204253530802}, + {-31.2774318502685, 613.426120316623}, + {-26.5391748405779, 613.426120316623}}), + R"json({"_type":"shape","interior":{"_type":"gentrap","halfedges":[0.501588152875291,0.5,51.400000000000006],"phi":0.0,"theta":0.22584674950181247},"label":"LArEMECInnerWheelAbsorber02"})json", + { + {51.2, 0.40, 7.76}, + {51.4, 0.51, 7.78}, + }), + celeritas::RuntimeError); + } +} + TEST_F(SolidConverterTest, intersectionsolid) { G4Box b1("Test Box #1", 20, 30, 40); @@ -313,6 +398,51 @@ TEST_F(SolidConverterTest, subtractionsolid) {{0, 0, 0}, {0, 0, 10}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}}); } +TEST_F(SolidConverterTest, trap) +{ + { + this->build_and_test( + G4Trap("boxTrap", 30, 0, 0, 20, 10, 10, 0, 20, 10, 10, 0), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":3.0, + "lower":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]], + "upper":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]]}, + "label":"boxTrap"})json", + {{-1, -2, -3}, {1, 2, 3}, {1, 2, 4}}); + } + + { + this->build_and_test( + G4Trap("trdGenTrap", 50, 100, 100, 200, 300), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":30.0, + "lower":[[-5.0,-10.0],[5.0,-10.0],[5.0,10.0],[-5.0,10.0]], + "upper":[[-10.0,-20.0],[10.0,-20.0],[10.0,20.0],[-10.0,20.0]]}, + "label":"trdGenTrap"})json", + {{-10, -20, -40}, + {-10, -20, -30 + 1.e-6}, + {5, 10, 30}, + {10, 10, 30}}); + } + + { + this->build_and_test( + G4Trap("trap", 40, 0.02, 0.05, 20, 10, 10, 0.01, 30, 15, 15, 0.01), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":4.0, + "lower":[[-1.0999113425658524,-2.0039988667381046], + [0.9000886574341476,-2.0039988667381046], + [0.9400899908208165,1.9960011332618952], + [-1.0599100091791835,1.9960011332618952]], + "upper":[[-1.4500903241674836,-2.9960011332618954], + [1.5499096758325164,-2.9960011332618954], + [1.6099116759125196,3.0039988667381046], + [-1.3900883240874804,3.0039988667381046]]}, + "label":"trap"})json", + {{-1, -2, -4 - 1.e-6}, {-1, -2, -3}, {0.5, 1, 3}, {1, 1, 3}}); + } +} + TEST_F(SolidConverterTest, tubs) { this->build_and_test( From 61907d90c34c71c6bf60c50a8e4b661589cde05d Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 1 May 2024 13:45:22 -0400 Subject: [PATCH 32/59] Add celer-geo app for visualizing geometry (#1201) --- app/CMakeLists.txt | 2 +- app/celer-g4/CMakeLists.txt | 13 +- .../CMakeLists.txt | 59 ++-- app/celer-geo/GeoInput.cc | 98 ++++++ app/celer-geo/GeoInput.hh | 64 ++++ app/celer-geo/README.md | 6 + app/celer-geo/Runner.cc | 233 +++++++++++++ app/celer-geo/Runner.hh | 106 ++++++ app/celer-geo/Types.cc | 42 +++ app/celer-geo/Types.hh | 83 +++++ app/celer-geo/celer-geo.cc | 317 ++++++++++++++++++ app/celer-geo/celer-geo.nojson.cc | 20 ++ app/celer-geo/test-harness.py | 93 +++++ app/celer-sim/Runner.cc | 1 + app/demo-rasterizer/demo-rasterizer.cc | 210 ------------ app/demo-rasterizer/simple-driver.py | 61 ---- app/demo-rasterizer/visualize.py | 37 -- doc/main/usage.rst | 58 ++++ 18 files changed, 1165 insertions(+), 338 deletions(-) rename app/{demo-rasterizer => celer-geo}/CMakeLists.txt (62%) create mode 100644 app/celer-geo/GeoInput.cc create mode 100644 app/celer-geo/GeoInput.hh create mode 100644 app/celer-geo/README.md create mode 100644 app/celer-geo/Runner.cc create mode 100644 app/celer-geo/Runner.hh create mode 100644 app/celer-geo/Types.cc create mode 100644 app/celer-geo/Types.hh create mode 100644 app/celer-geo/celer-geo.cc create mode 100644 app/celer-geo/celer-geo.nojson.cc create mode 100755 app/celer-geo/test-harness.py delete mode 100644 app/demo-rasterizer/demo-rasterizer.cc delete mode 100755 app/demo-rasterizer/simple-driver.py delete mode 100755 app/demo-rasterizer/visualize.py diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 53c9e010ce..73929470c2 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -79,11 +79,11 @@ celeritas_target_link_libraries(celer-dump-data add_subdirectory(celer-sim) add_subdirectory(celer-g4) +add_subdirectory(celer-geo) if(CELERITAS_BUILD_DEMOS) add_subdirectory(demo-interactor) add_subdirectory(demo-geo-check) - add_subdirectory(demo-rasterizer) endif() #-----------------------------------------------------------------------------# diff --git a/app/celer-g4/CMakeLists.txt b/app/celer-g4/CMakeLists.txt index d633e50161..7905c94e6e 100644 --- a/app/celer-g4/CMakeLists.txt +++ b/app/celer-g4/CMakeLists.txt @@ -32,6 +32,12 @@ if(CELERITAS_USE_Geant4) nlohmann_json::nlohmann_json ) endif() + if(CELERITAS_USE_ROOT) + list(APPEND SOURCES + RootIO.cc + ) + list(APPEND LIBRARIES ROOT::Tree) + endif() celeritas_get_g4libs(_g4_libs geometry persistency intercoms run tasking physicslists interfaces) list(APPEND LIBRARIES ${_g4_libs}) @@ -42,13 +48,6 @@ else() set(LIBRARIES Celeritas::corecel) endif() -if(CELERITAS_USE_ROOT AND CELERITAS_USE_Geant4) - list(APPEND SOURCES - RootIO.cc - ) - list(APPEND LIBRARIES ROOT::Tree) -endif() - celeritas_add_executable(celer-g4 ${SOURCES}) celeritas_target_link_libraries(celer-g4 ${LIBRARIES} diff --git a/app/demo-rasterizer/CMakeLists.txt b/app/celer-geo/CMakeLists.txt similarity index 62% rename from app/demo-rasterizer/CMakeLists.txt rename to app/celer-geo/CMakeLists.txt index 25d11720a6..6e58f1a4b8 100644 --- a/app/demo-rasterizer/CMakeLists.txt +++ b/app/celer-geo/CMakeLists.txt @@ -4,26 +4,35 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) #-----------------------------------------------------------------------------# -set(SOURCES - demo-rasterizer.cc -) -set(_rasterizer_libs - Celeritas::corecel - Celeritas::geocel - Celeritas::orange - nlohmann_json::nlohmann_json -) -if(CELERITAS_CORE_GEO STREQUAL "ORANGE") - list(APPEND _rasterizer_libs Celeritas::orange) -elseif(CELERITAS_CORE_GEO STREQUAL "VecGeom") - list(APPEND _rasterizer_libs VecGeom::vecgeom) -elseif(CELERITAS_CORE_GEO STREQUAL "Geant4") - celeritas_get_g4libs(_g4_geo_libs geometry) - list(APPEND _rasterizer_libs ${_g4_geo_libs}) +if(CELERITAS_USE_JSON) + set(SOURCES + celer-geo.cc + Types.cc + GeoInput.cc + Runner.cc + ) + set(LIBRARIES + Celeritas::corecel + Celeritas::geocel + Celeritas::orange + nlohmann_json::nlohmann_json + ) + if(CELERITAS_USE_VecGeom) + list(APPEND LIBRARIES VecGeom::vecgeom) + endif() + if(CELERITAS_USE_Geant4) + celeritas_get_g4libs(_g4_geo_libs geometry) + list(APPEND LIBRARIES ${_g4_geo_libs}) + endif() +else() + set(SOURCES + celer-geo.nojson.cc + ) + set(LIBRARIES Celeritas::corecel) endif() -add_executable(demo-rasterizer ${SOURCES}) -celeritas_target_link_libraries(demo-rasterizer ${_rasterizer_libs}) +celeritas_add_executable(celer-geo ${SOURCES}) +celeritas_target_link_libraries(celer-geo ${LIBRARIES}) #-----------------------------------------------------------------------------# # TESTS @@ -33,12 +42,18 @@ if(NOT BUILD_TESTING) return() endif() -set(_driver "${CMAKE_CURRENT_SOURCE_DIR}/simple-driver.py") +set(_driver "${CMAKE_CURRENT_SOURCE_DIR}/test-harness.py") set(_model "${CELER_APP_DATA_DIR}/simple-cms.gdml") set(_env - "CELERITAS_DEMO_EXE=$" + "CELERITAS_DEMO_EXE=$" "CELER_DISABLE_PARALLEL=1" ) +set(_props + TIMEOUT 20 +) +if(NOT CELERITAS_USE_JSON) + list(APPEND _props DISABLED true) +endif() #-----------------------------------------------------------------------------# function(celer_app_test ext) @@ -55,11 +70,11 @@ function(celer_app_test ext) list(APPEND _extra_env "CELER_DISABLE_DEVICE=1") endif() - add_test(NAME "app/demo-rasterizer:${ext}" + add_test(NAME "app/celer-geo:${ext}" COMMAND "${CELER_PYTHON}" "${_driver}" "${_model}" ) - set_tests_properties("app/demo-rasterizer:${ext}" PROPERTIES + set_tests_properties("app/celer-geo:${ext}" PROPERTIES ENVIRONMENT "${_env};${_extra_env}" REQUIRED_FILES "${_driver};${_gdml_inp}" LABELS "${_labels}" diff --git a/app/celer-geo/GeoInput.cc b/app/celer-geo/GeoInput.cc new file mode 100644 index 0000000000..062924f7e6 --- /dev/null +++ b/app/celer-geo/GeoInput.cc @@ -0,0 +1,98 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/GeoInput.cc +//---------------------------------------------------------------------------// +#include "GeoInput.hh" + +#include "corecel/Types.hh" +#include "corecel/io/JsonUtils.json.hh" +#include "corecel/io/StringEnumMapper.hh" + +namespace celeritas +{ +namespace app +{ +namespace +{ +//---------------------------------------------------------------------------// +/*! + * Convert a geometry string to an enum for JSON input. + */ +Geometry to_geometry(std::string const& s) +{ + static auto const from_string + = StringEnumMapper::from_cstring_func(to_cstring, "geometry"); + return from_string(s); +} + +//---------------------------------------------------------------------------// +/*! + * Convert a memspace string to an enum for JSON input. + */ +MemSpace to_memspace(std::string const& s) +{ + static auto const from_string + = StringEnumMapper::from_cstring_func( + ::celeritas::to_cstring, "memspace"); + return from_string(s); +} + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +#define GI_LOAD_OPTION(NAME) CELER_JSON_LOAD_OPTION(j, v, NAME) +#define GI_LOAD_REQUIRED(NAME) CELER_JSON_LOAD_REQUIRED(j, v, NAME) +#define GI_SAVE_NONZERO(NAME) CELER_JSON_SAVE_WHEN(j, v, NAME, v.NAME != 0) +#define GI_SAVE(NAME) CELER_JSON_SAVE(j, v, NAME) + +void from_json(nlohmann::json const& j, ModelSetup& v) +{ + CELER_VALIDATE(j.is_object(), + << "input JSON for ModelSetup is not an object: '" + << j.dump() << '\''); + GI_LOAD_OPTION(cuda_stack_size); + GI_LOAD_OPTION(cuda_heap_size); + GI_LOAD_REQUIRED(geometry_file); +} + +void from_json(nlohmann::json const& j, TraceSetup& v) +{ + if (auto iter = j.find("geometry"); iter != j.end()) + { + v.geometry = to_geometry(iter->get()); + } + if (auto iter = j.find("memspace"); iter != j.end()) + { + v.memspace = to_memspace(iter->get()); + } + GI_LOAD_OPTION(volumes); + GI_LOAD_REQUIRED(bin_file); +} + +void to_json(nlohmann::json& j, ModelSetup const& v) +{ + GI_SAVE_NONZERO(cuda_stack_size); + GI_SAVE_NONZERO(cuda_heap_size); + GI_SAVE(geometry_file); +} + +void to_json(nlohmann::json& j, TraceSetup const& v) +{ + j["geometry"] = to_cstring(v.geometry); + j["memspace"] = to_cstring(v.memspace); + GI_SAVE(volumes); + GI_SAVE(bin_file); +} + +#undef GI_LOAD_OPTION +#undef GI_LOAD_REQUIRED +#undef GI_SAVE_NONZERO +#undef GI_SAVE + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-geo/GeoInput.hh b/app/celer-geo/GeoInput.hh new file mode 100644 index 0000000000..eabf4d7432 --- /dev/null +++ b/app/celer-geo/GeoInput.hh @@ -0,0 +1,64 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/GeoInput.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "corecel/Types.hh" + +#include "Types.hh" + +namespace celeritas +{ +namespace app +{ +//---------------------------------------------------------------------------// +/*! + * Input for setting up the problem for multiple raytraces. + */ +struct ModelSetup +{ + // Global environment + size_type cuda_stack_size{}; + size_type cuda_heap_size{}; + + //! Geometry filename to load (usually GDML) + std::string geometry_file; +}; + +//---------------------------------------------------------------------------// +/*! + * Input for generating a raytrace. + */ +struct TraceSetup +{ + //! Rendering geometry + Geometry geometry{default_geometry()}; + + //! Rendering memory space + MemSpace memspace{default_memspace()}; + + //! Whether to output volume names for this geometry + bool volumes{false}; + + //! Output filename for binary + std::string bin_file; +}; + +//---------------------------------------------------------------------------// + +void to_json(nlohmann::json& j, ModelSetup const& value); +void from_json(nlohmann::json const& j, ModelSetup& value); + +void to_json(nlohmann::json& j, TraceSetup const& value); +void from_json(nlohmann::json const& j, TraceSetup& value); + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-geo/README.md b/app/celer-geo/README.md new file mode 100644 index 0000000000..cd20ba0509 --- /dev/null +++ b/app/celer-geo/README.md @@ -0,0 +1,6 @@ +Note: glasbey-cmap.txt is derived from colormaps at +https://github.com/holoviz/colorcet/pull/25 under a CC-BY 4.0 license + +```python +vals = pd.read_csv("https://raw.githubusercontent.com/holoviz/colorcet/master/assets/Glasbey/glasbey_bw_minc_20_minl_30_n256.csv", header=None) +``` diff --git a/app/celer-geo/Runner.cc b/app/celer-geo/Runner.cc new file mode 100644 index 0000000000..f1c56157e8 --- /dev/null +++ b/app/celer-geo/Runner.cc @@ -0,0 +1,233 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/Runner.cc +//---------------------------------------------------------------------------// +#include "Runner.hh" + +#include "celeritas_config.h" +#include "corecel/io/StringUtils.hh" +#include "corecel/sys/Device.hh" +#include "corecel/sys/Stopwatch.hh" +#include "geocel/rasterize/RaytraceImager.hh" +#include "orange/OrangeParams.hh" +#if CELERITAS_USE_GEANT4 +# include "geocel/g4/GeantGeoParams.hh" +#endif +#if CELERITAS_USE_VECGEOM +# include "geocel/vg/VecgeomParams.hh" +#endif + +#define CASE_RETURN_FUNC_T(T, FUNC, ...) \ + case T: \ + return this->FUNC(__VA_ARGS__) + +namespace celeritas +{ +namespace app +{ +//---------------------------------------------------------------------------// +/*! + * Construct with model setup. + */ +Runner::Runner(ModelSetup const& input) : input_{input} +{ + // Initialize GPU + activate_device(); + + if (input_.cuda_heap_size) + { + set_cuda_heap_size(input_.cuda_heap_size); + } + if (input_.cuda_stack_size) + { + set_cuda_stack_size(input_.cuda_stack_size); + } + + if (CELERITAS_USE_GEANT4 && ends_with(input_.geometry_file, ".gdml")) + { + // Retain the Geant4 world for possible reuse across geometries + this->load_geometry(); + } +} + +//---------------------------------------------------------------------------// +/*! + * Perform a raytrace. + */ +auto Runner::operator()(TraceSetup const& trace, ImageInput const& image_inp) + -> SPImage +{ + // Create image params + last_image_ = std::make_shared(image_inp); + + return (*this)(trace); +} + +//---------------------------------------------------------------------------// +/*! + * Perform a raytrace using the last image but a new geometry/memspace. + * + * The memory space in Celeritas is the same as the execution space. + */ +auto Runner::operator()(TraceSetup const& trace) -> SPImage +{ + CELER_VALIDATE(last_image_, + << "first trace input did not specify an image"); + + // Load geometry + SPImager imager = this->make_imager(trace.geometry); + + // Create image + SPImage image = this->make_traced_image(trace.memspace, *imager); + return image; +} +//---------------------------------------------------------------------------// +/*! + * Get volume names from an already loaded geometry. + */ +std::vector Runner::get_volumes(Geometry g) const& +{ + CELER_EXPECT(geo_cache_[g]); + + auto const& geo = *geo_cache_[g]; + std::vector result(geo.num_volumes()); + for (auto i : range(result.size())) + { + result[i] = geo.id_to_label(VolumeId{i}).name; + } + return result; +} + +//---------------------------------------------------------------------------// +// HELPER FUNCTIONS +//---------------------------------------------------------------------------// +/*! + * Load a geometry, caching it. + * + * If Geant4 is available and the input file is GDML, this will be executed + * when the runner is constructed to save a reusable pointer to the Geant4 + * world. Otherwise, this is called by the imager when raytracing a new + * geometry type. + */ +template +auto Runner::load_geometry() -> std::shared_ptr const> +{ + using GP = GeoParams_t; + + auto& cached = geo_cache_[G]; + std::shared_ptr geo; + if constexpr (!is_geometry_configured_v) + { + CELER_NOT_CONFIGURED(to_cstring(G)); + } + else if (cached) + { + // Downcast to type + geo = std::dynamic_pointer_cast(cached); + } + else + { + Stopwatch get_time; + if (geant_world_) + { + // Load from existing Geant4 geometry + geo = std::make_shared(geant_world_); + } + else + { + // Load directly from input file + geo = std::make_shared(input_.geometry_file); + if constexpr (G == Geometry::geant4) + { + // Save world for later reuse + geant_world_ = static_cast(*geo).world(); + } + } + // Save load time + timers_[std::string{"load_"} + to_cstring(G)] = get_time(); + + // Save geometry in cache + cached = geo; + } + + CELER_ENSURE(cached); + CELER_ENSURE(geo); + return geo; +} + +//---------------------------------------------------------------------------// +/*! + * Create a tracer from an enumeration. + */ +auto Runner::make_imager(Geometry g) -> SPImager +{ + imager_name_ = std::string{"raytrace_"} + to_cstring(g); + switch (g) + { + CASE_RETURN_FUNC_T(Geometry::orange, make_imager, ); + CASE_RETURN_FUNC_T(Geometry::vecgeom, make_imager, ); + CASE_RETURN_FUNC_T(Geometry::geant4, make_imager, ); + default: + CELER_ASSERT_UNREACHABLE(); + } +} + +//---------------------------------------------------------------------------// +/*! + * Create a tracer of a given type. + */ +template +auto Runner::make_imager() -> SPImager +{ + using GP = GeoParams_t; + + if constexpr (is_geometry_configured_v) + { + static_assert(is_geometry_configured_v); + std::shared_ptr geo = this->load_geometry(); + return std::make_shared>(geo); + } + else + { + CELER_NOT_CONFIGURED(to_cstring(G)); + } +} + +//---------------------------------------------------------------------------// +/*! + * Allocate and perform a raytrace using an enumeration. + */ +auto Runner::make_traced_image(MemSpace m, ImagerInterface& generate_image) + -> SPImage +{ + switch (m) + { + CASE_RETURN_FUNC_T(MemSpace::host, make_traced_image, generate_image); + CASE_RETURN_FUNC_T(MemSpace::device, make_traced_image, generate_image); + default: + CELER_ASSERT_UNREACHABLE(); + } +} + +//---------------------------------------------------------------------------// +/*! + * Allocate and perform a raytrace with the given memory/execution space. + */ +template +auto Runner::make_traced_image(ImagerInterface& generate_image) -> SPImage +{ + auto image = std::make_shared>(last_image_); + + Stopwatch get_time; + generate_image(image.get()); + timers_[imager_name_ + '_' + to_cstring(M)] += get_time(); + + return image; +} + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-geo/Runner.hh b/app/celer-geo/Runner.hh new file mode 100644 index 0000000000..94281fa03f --- /dev/null +++ b/app/celer-geo/Runner.hh @@ -0,0 +1,106 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/Runner.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "corecel/cont/EnumArray.hh" +#include "geocel/GeoParamsInterface.hh" +#include "geocel/rasterize/Image.hh" + +#include "GeoInput.hh" +#include "Types.hh" + +class G4VPhysicalVolume; + +namespace celeritas +{ +namespace app +{ +//---------------------------------------------------------------------------// +/*! + * Set up and run rasterization, caching as needed. + * + * Each geometry instance is loaded when requested. If Geant4 is enabled, and a + * GDML file is loaded, the Geant4 geometry model will be loaded *first* and + * used to perform in-memory conversion. + * + * The first call to the runner \em must set an image using the call operator + * that takes \c ImageInput, but subsequent calls will reuse the same image. + * This is useful for comparing that multiple geometries are rendering the same + * geometry identically. + */ +class Runner +{ + public: + //!@{ + //! \name Type aliases + using SPImage = std::shared_ptr; + using MapTimers = std::map; + //!@} + + public: + // Construct with model setup + explicit Runner(ModelSetup const& input); + + // Perform a raytrace + SPImage operator()(TraceSetup const&, ImageInput const&); + + // Perform a raytrace using the last image but a new geometry + SPImage operator()(TraceSetup const&); + + //! Access timers + MapTimers const& timers() const { return timers_; } + + //! Access volumes + std::vector get_volumes(Geometry) const&; + + private: + //// TYPES //// + + using SPConstGeometry = std::shared_ptr; + using SPImageParams = std::shared_ptr; + using SPImager = std::shared_ptr; + + template + using GeoArray = EnumArray; + + //// DATA //// + + ModelSetup input_; + GeoArray geo_cache_; + SPImageParams last_image_; + std::string imager_name_; + G4VPhysicalVolume const* geant_world_{nullptr}; + MapTimers timers_; + + //// HELPER FUNCTIONS //// + + // Load a geometry + template + std::shared_ptr const> load_geometry(); + + // Create a tracer + SPImager make_imager(Geometry); + + // Create a tracer + template + SPImager make_imager(); + + // Allocate and perform a raytrace + SPImage make_traced_image(MemSpace, ImagerInterface& generate_image); + + // Allocate and perform a raytrace + template + SPImage make_traced_image(ImagerInterface& generate_image); +}; + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-geo/Types.cc b/app/celer-geo/Types.cc new file mode 100644 index 0000000000..7c9b9c60fc --- /dev/null +++ b/app/celer-geo/Types.cc @@ -0,0 +1,42 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/Types.cc +//---------------------------------------------------------------------------// +#include "Types.hh" + +#include "corecel/io/EnumStringMapper.hh" +#include "corecel/sys/Device.hh" + +namespace celeritas +{ +namespace app +{ +//---------------------------------------------------------------------------// +/*! + * Convert a geometry enum to a string. + */ +char const* to_cstring(Geometry value) +{ + static EnumStringMapper const to_cstring_impl{ + "orange", + "vecgeom", + "geant4", + }; + return to_cstring_impl(value); +} + +//---------------------------------------------------------------------------// +/*! + * Default memory space for rendering. + */ +MemSpace default_memspace() +{ + return celeritas::device() ? MemSpace::device : MemSpace::host; +} + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-geo/Types.hh b/app/celer-geo/Types.hh new file mode 100644 index 0000000000..f8cf258ced --- /dev/null +++ b/app/celer-geo/Types.hh @@ -0,0 +1,83 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/Types.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas_config.h" +#include "corecel/Types.hh" +#include "geocel/g4/GeantGeoTraits.hh" +#include "geocel/vg/VecgeomGeoTraits.hh" +#include "orange/OrangeGeoTraits.hh" + +namespace celeritas +{ +namespace app +{ +//---------------------------------------------------------------------------// +//! Geometry to be used for ray tracing +enum class Geometry +{ + orange, + vecgeom, + geant4, + size_ +}; + +//---------------------------------------------------------------------------// +/*! + * Get the user-facing GeoParams class from a Geometry enum. + */ +template +struct GeoTraitsFromEnum; + +template<> +struct GeoTraitsFromEnum +{ + using type = OrangeParams; +}; + +template<> +struct GeoTraitsFromEnum +{ + using type = VecgeomParams; +}; + +template<> +struct GeoTraitsFromEnum +{ + using type = GeantGeoParams; +}; + +//---------------------------------------------------------------------------// +//! Type alias for accessing GeoParams from a Geometry enum +template +using GeoParams_t = typename GeoTraitsFromEnum::type; + +//---------------------------------------------------------------------------// +// FUNCTIONS +//---------------------------------------------------------------------------// + +// Convert a geometry enum to a string +char const* to_cstring(Geometry value); + +// Default memory space for rendering +MemSpace default_memspace(); + +//! Default geometry type for rendering +inline constexpr Geometry default_geometry() +{ + // clang-format off + return CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_ORANGE ? Geometry::orange + : CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_VECGEOM ? Geometry::vecgeom + : CELERITAS_CORE_GEO == CELERITAS_CORE_GEO_GEANT4 ? Geometry::geant4 + : Geometry::size_; + // clang-format on +} + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-geo/celer-geo.cc b/app/celer-geo/celer-geo.cc new file mode 100644 index 0000000000..5275043b4d --- /dev/null +++ b/app/celer-geo/celer-geo.cc @@ -0,0 +1,317 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/celer-geo.cc +//---------------------------------------------------------------------------// +#include +#include +#include +#include +#include +#include +#include + +#include "celeritas_config.h" +#include "celeritas_version.h" +#include "corecel/io/ExceptionOutput.hh" +#include "corecel/io/Logger.hh" +#include "corecel/io/Repr.hh" +#include "corecel/io/StringUtils.hh" +#include "corecel/sys/Device.hh" +#include "corecel/sys/DeviceIO.json.hh" +#include "corecel/sys/KernelRegistry.hh" +#include "corecel/sys/KernelRegistryIO.json.hh" +#include "corecel/sys/MpiCommunicator.hh" +#include "corecel/sys/ScopedMpiInit.hh" +#include "corecel/sys/ScopedSignalHandler.hh" +#include "geocel/rasterize/Image.hh" +#include "geocel/rasterize/ImageIO.json.hh" + +#include "GeoInput.hh" +#include "Runner.hh" + +using namespace std::literals::string_view_literals; +using nlohmann::json; + +namespace celeritas +{ +namespace app +{ +namespace +{ +//---------------------------------------------------------------------------// +/*! + * Get a line of JSON input. + */ +nlohmann::json get_json_line(std::istream& is) +{ + static std::string jsonline; + + // TODO: separate thread for cin to check periodically for interrupts + if (!std::getline(is, jsonline)) + { + CELER_LOG(debug) << "Reached end of file"; + return nullptr; + } + if (trim(jsonline).empty()) + { + CELER_LOG(debug) << "Got empty line"; + // No input provided + return nullptr; + } + + try + { + return json::parse(jsonline); + } + catch (json::parse_error const& e) + { + CELER_LOG(error) << "Failed to parse JSON input: " << e.what(); + CELER_LOG(info) << "Failed line: " << repr(jsonline); + return nullptr; + } +} + +//---------------------------------------------------------------------------// +/*! + * Create a Runner from user input. + */ +Runner make_runner(json const& input) +{ + ModelSetup model_setup; + try + { + input.get_to(model_setup); + } + catch (std::exception const& e) + { + CELER_LOG(error) << "Invalid model setup; expected structure written " + "to stdout"; + std::cout << json(ModelSetup{}).dump() << std::endl; + throw; + } + + Runner result(model_setup); + std::cout << json(model_setup) << std::endl; + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Execute a single raytrace. + */ +void run_trace(Runner& run_trace, + TraceSetup const& trace_setup, + ImageInput const& image_setup) +{ + CELER_LOG(status) << "Tracing " << to_cstring(trace_setup.geometry) + << " image on " << to_cstring(trace_setup.memspace); + + // Run the raytrace + auto image = [&] { + if (image_setup) + { + // User specified a new image setup + return run_trace(trace_setup, image_setup); + } + else + { + // Reuse last image setup + return run_trace(trace_setup); + } + }(); + CELER_ASSERT(image); + + auto const& img_params = *image->params(); + + // Write the output to disk + CELER_LOG(info) << "Writing image to '" << trace_setup.bin_file << '\''; + { + std::ofstream image_file(trace_setup.bin_file, std::ios::binary); + std::vector image_data(img_params.num_pixels()); + image->copy_to_host(make_span(image_data)); + image_file.write(reinterpret_cast(image_data.data()), + image_data.size() * sizeof(int)); + } + + json out{ + {"trace", trace_setup}, + {"image", img_params}, + {"sizeof_int", sizeof(int)}, + }; + if (trace_setup.volumes) + { + // Get geometry names + out["volumes"] = run_trace.get_volumes(trace_setup.geometry); + } + + std::cout << out.dump() << std::endl; +} + +//---------------------------------------------------------------------------// +/*! + * Run, launch, and output. + * + * The input stream is expected to be in "JSON lines" format. The first input + * \em must be a model setup; the following lines are individual commands to + * trace an image. Newlines must be sent exactly \em once per input, and the + * output \em must be flushed after doing so. (Recall that \em endl sends a + * newline and flushes the output buffer.) + */ +void run(std::istream& is) +{ + ScopedSignalHandler interrupted{SIGINT}; + + // Load the model + CELER_LOG(diagnostic) << "Waiting for model setup"; + auto json_input = get_json_line(is); + CELER_VALIDATE(json_input.is_object(), + << "missing or invalid JSON-formatted run input"); + + auto runner = make_runner(json_input); + + while (true) + { + json_input = get_json_line(is); + if (interrupted()) + { + CELER_LOG(diagnostic) << "Exiting raytrace loop: caught interrupt"; + interrupted = {}; + break; + } + if (json_input.is_null()) + { + CELER_LOG(diagnostic) << "Exiting raytrace loop"; + break; + } + + // Load required trace setup (geometry/memspace/output) + TraceSetup trace_setup; + ImageInput image_setup; + try + { + CELER_VALIDATE(!json_input.empty(), + << "no raytrace input was specified"); + json_input.get_to(trace_setup); + if (auto iter = json_input.find("image"); iter != json_input.end()) + { + iter->get_to(image_setup); + } + } + catch (std::exception const& e) + { + CELER_LOG(error) + << "Invalid trace setup; expected structure written " + "to stdout"; + json temp = TraceSetup{}; + temp["image"] = ImageInput{}; + std::cout << json(temp).dump() << std::endl; + continue; + } + try + { + run_trace(runner, trace_setup, image_setup); + } + catch (std::exception const& e) + { + CELER_LOG(error) << "Failed raytrace: " << e.what(); + std::cout << ExceptionOutput{std::current_exception()} << std::endl; + } + } + + // Construct json output (TODO: add build metadata) + std::cout << json{ + {"timers", runner.timers()}, + { + "runtime", + { + {"version", std::string(celeritas_version)}, + {"device", device()}, + {"kernels", kernel_registry()}, + }, + }, + } << std::endl; +} + +//---------------------------------------------------------------------------// +void print_usage(std::string_view exec_name) +{ + // clang-format off + std::cerr << "usage: " << exec_name << " {input}.json\n" + " " << exec_name << " -\n" + " " << exec_name << " [--help|-h]\n"; + // clang-format on +} + +//---------------------------------------------------------------------------// +} // namespace +} // namespace app +} // namespace celeritas + +//---------------------------------------------------------------------------// +/*! + * Execute and run. + */ +int main(int argc, char* argv[]) +{ + using namespace celeritas; + + ScopedMpiInit scoped_mpi(&argc, &argv); + if (ScopedMpiInit::status() == ScopedMpiInit::Status::initialized + && MpiCommunicator::comm_world().size() > 1) + { + CELER_LOG(critical) << "This app cannot run in parallel"; + return EXIT_FAILURE; + } + + // Process input arguments + if (argc != 2) + { + celeritas::app::print_usage(argv[0]); + return EXIT_FAILURE; + } + std::string_view filename{argv[1]}; + if (filename == "--help"sv || filename == "-h"sv) + { + celeritas::app::print_usage(argv[0]); + return EXIT_SUCCESS; + } + + // TODO: make a helper class that implicitly casts to std::istream&, has + // operator bool, and retains a file or creates with '-' + std::ifstream infile; + std::istream* instream = nullptr; + if (filename == "-") + { + instream = &std::cin; + filename = ""; // For nicer output on failure + } + else + { + // Open the specified file + infile.open(std::string{filename}); + if (!infile) + { + CELER_LOG(critical) << "Failed to open '" << filename << "'"; + return EXIT_FAILURE; + } + instream = &infile; + } + CELER_LOG(info) << "Reading JSON line input from " << filename; + + try + { + celeritas::app::run(*instream); + } + catch (std::exception const& e) + { + CELER_LOG(critical) + << "While running input at " << filename << ": " << e.what(); + std::cout << ExceptionOutput{std::current_exception()} << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/app/celer-geo/celer-geo.nojson.cc b/app/celer-geo/celer-geo.nojson.cc new file mode 100644 index 0000000000..47bef98dba --- /dev/null +++ b/app/celer-geo/celer-geo.nojson.cc @@ -0,0 +1,20 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-geo/celer-geo.nojson.cc +//---------------------------------------------------------------------------// +#include +#include + +//---------------------------------------------------------------------------// +/*! + * Execute and run. + */ +int main(int, char* argv[]) +{ + std::cerr << argv[0] << ": JSON is not enabled in this build of Celeritas" + << std::endl; + return EXIT_FAILURE; +} diff --git a/app/celer-geo/test-harness.py b/app/celer-geo/test-harness.py new file mode 100755 index 0000000000..8fc3643548 --- /dev/null +++ b/app/celer-geo/test-harness.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +# See the top-level COPYRIGHT file for details. +# SPDX-License-Identifier: (Apache-2.0 OR MIT) +""" +""" +import json +import subprocess +from os import environ +from sys import exit, argv +from pathlib import Path + +try: + (model_file,) = argv[1:] +except TypeError: + print("usage: {} inp.gdml".format(sys.argv[0])) + exit(2) + +def decode_line(jsonline): + try: + return json.loads(jsonline) + except json.decoder.JSONDecodeError as e: + print("error: expected a JSON object but got the following stdout:") + print(jsonline) + print("fatal:", str(e)) + exit(1) + +exe = environ.get("CELERITAS_DEMO_EXE", "./demo-rasterizer") +ext = environ.get("CELER_TEST_EXT", "unknown") + +problem_name = "-".join([Path(model_file).stem, ext]) + +image = { + "_units": "cgs", + "_units": "cgs", + "lower_left": [-800, 0, -1500], + "upper_right": [800, 0, 1600], + "rightward": [1, 0, 0], + "vertical_pixels": 128, +} + +commands = [ + { + "geometry_file": model_file, + }, + { + "image": image, + "volumes": True, + "bin_file": f"{problem_name}.orange.bin", + }, + { + # Reuse image setup + "bin_file": f"{problem_name}.geant4.bin", + "geometry": "geant4", + }, + { + "bin_file": f"{problem_name}.vecgeom.bin", + "geometry": "vecgeom", + }, +] + +filename = f"{problem_name}.inp.jsonl" +with open(filename, 'w') as f: + for c in commands: + json.dump(c, f) + f.write('\n') +print("Wrote input to", filename) + +print("Running", exe) +result = subprocess.run([exe, filename], + stdout=subprocess.PIPE) +if result.returncode: + print("Run failed with error", result.returncode) + exit(result.returncode) + +print("Received {} bytes of data".format(len(result.stdout))) +with open(f'{problem_name}.out.json', 'wb') as f: + f.write(result.stdout) +out_lines = result.stdout.decode().splitlines() + +# Geometry diagnostic information +print(decode_line(out_lines[0])) + +for line in out_lines[1:-1]: + result = decode_line(line) + if result.get("_label") == "exception": + # Note that it's *OK* for the trace to fail e.g. if we have disabled + # vecgeom or GPU + print("Ray trace failed:") + print(json.dumps(result, indent=1)) + +print(json.dumps(decode_line(out_lines[-1]), indent=1)) diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 34fab17d77..2394278584 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -228,6 +228,7 @@ auto Runner::get_action_times() const -> MapStrDouble //---------------------------------------------------------------------------// void Runner::setup_globals(RunnerInput const& inp) const { + // TODO: just use 0 instead of unspecified if (inp.cuda_heap_size != RunnerInput::unspecified) { set_cuda_heap_size(inp.cuda_heap_size); diff --git a/app/demo-rasterizer/demo-rasterizer.cc b/app/demo-rasterizer/demo-rasterizer.cc deleted file mode 100644 index 677ed40d49..0000000000 --- a/app/demo-rasterizer/demo-rasterizer.cc +++ /dev/null @@ -1,210 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file demo-rasterizer/demo-rasterizer.cc -//---------------------------------------------------------------------------// -#include -#include -#include -#include -#include -#include - -#include "celeritas_config.h" -#include "celeritas_version.h" -#include "corecel/io/Logger.hh" -#include "corecel/sys/Device.hh" -#include "corecel/sys/DeviceIO.json.hh" -#include "corecel/sys/KernelRegistry.hh" -#include "corecel/sys/KernelRegistryIO.json.hh" -#include "corecel/sys/MpiCommunicator.hh" -#include "corecel/sys/ScopedMpiInit.hh" -#include "corecel/sys/Stopwatch.hh" -#include "geocel/GeoParamsInterface.hh" -#include "geocel/rasterize/Image.hh" -#include "geocel/rasterize/ImageIO.json.hh" -#include "geocel/rasterize/RaytraceImager.hh" - -// TODO: replace with factory -#include "celeritas/geo/GeoParams.hh" - -namespace celeritas -{ -namespace app -{ -namespace -{ -//---------------------------------------------------------------------------// -using SPConstGeometry = std::shared_ptr; -using SPImager = std::shared_ptr; - -std::pair -load_geometry_imager(std::string const& filename) -{ - auto geo = std::make_shared(filename); - auto imager = std::make_shared>(geo); - return {std::move(geo), std::move(imager)}; -} - -template -std::shared_ptr> -make_traced_image(std::shared_ptr img_params, - ImagerInterface& generate_image) -{ - auto image = std::make_shared>(std::move(img_params)); - CELER_LOG(status) << "Tracing image on " << to_cstring(M); - generate_image(image.get()); - return image; -} - -//---------------------------------------------------------------------------// -/*! - * Run, launch, and output. - */ -void run(std::istream& is) -{ - // Read input options - auto inp = nlohmann::json::parse(is); - auto timers = nlohmann::json::object(); - - if (inp.contains("cuda_heap_size")) - { - set_cuda_heap_size(inp.at("cuda_heap_size").get()); - } - if (inp.contains("cuda_stack_size")) - { - set_cuda_stack_size(inp.at("cuda_stack_size").get()); - } - - // Load geometry - Stopwatch get_time; - auto&& [geo_params, generate_image] - = load_geometry_imager(inp.at("input").get().c_str()); - timers["load"] = get_time(); - - // Construct image - auto img_params - = std::make_shared(inp.at("image").get()); - - get_time = {}; - std::shared_ptr image; - if (device()) - { - image - = make_traced_image(img_params, *generate_image); - } - else - { - image = make_traced_image(img_params, *generate_image); - } - CELER_ASSERT(image); - timers["trace"] = get_time(); - - // Get geometry names - std::vector vol_names; - for (auto vol_id : range(geo_params->num_volumes())) - { - vol_names.push_back(geo_params->id_to_label(VolumeId(vol_id)).name); - } - - // Write image - CELER_LOG(status) << "Transferring image and writing to disk"; - get_time = {}; - - std::string out_filename = inp.at("output"); - std::vector image_data(img_params->num_pixels()); - image->copy_to_host(make_span(image_data)); - std::ofstream(out_filename, std::ios::binary) - .write(reinterpret_cast(image_data.data()), - image_data.size() * sizeof(int)); - timers["write"] = get_time(); - - // Construct json output - CELER_LOG(status) << "Exporting JSON metadata"; - nlohmann::json outp = { - {"metadata", *img_params}, - {"data", out_filename}, - {"volumes", vol_names}, - {"timers", timers}, - { - "runtime", - { - {"version", std::string(celeritas_version)}, - {"device", device()}, - {"kernels", kernel_registry()}, - }, - }, - }; - outp["metadata"]["int_size"] = sizeof(int); - std::cout << outp.dump() << std::endl; - CELER_LOG(info) << "Exported image to " << out_filename; -} - -//---------------------------------------------------------------------------// -} // namespace -} // namespace app -} // namespace celeritas - -//---------------------------------------------------------------------------// -/*! - * Execute and run. - * - * \todo This is copied from the other demo apps; move these to a shared driver - * at some point. - */ -int main(int argc, char* argv[]) -{ - using namespace celeritas; - - ScopedMpiInit scoped_mpi(&argc, &argv); - if (ScopedMpiInit::status() == ScopedMpiInit::Status::initialized - && MpiCommunicator::comm_world().size() > 1) - { - CELER_LOG(critical) << "This app cannot run in parallel"; - return EXIT_FAILURE; - } - - // Process input arguments - std::vector args(argv, argv + argc); - if (args.size() != 2 || args[1] == "--help" || args[1] == "-h") - { - std::cerr << "usage: " << args[0] << " {input}.json" << std::endl; - return EXIT_FAILURE; - } - - std::ifstream infile; - std::istream* instream_ptr = nullptr; - if (args[1] != "-") - { - infile.open(args[1]); - if (!infile) - { - CELER_LOG(critical) << "Failed to open '" << args[1] << "'"; - return EXIT_FAILURE; - } - instream_ptr = &infile; - } - else - { - // Read input from STDIN - instream_ptr = &std::cin; - } - - // Initialize GPU - activate_device(); - - try - { - CELER_ASSERT(instream_ptr); - celeritas::app::run(*instream_ptr); - } - catch (std::exception const& e) - { - CELER_LOG(critical) << "caught exception: " << e.what(); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/app/demo-rasterizer/simple-driver.py b/app/demo-rasterizer/simple-driver.py deleted file mode 100755 index 0ba6f94ff9..0000000000 --- a/app/demo-rasterizer/simple-driver.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -# See the top-level COPYRIGHT file for details. -# SPDX-License-Identifier: (Apache-2.0 OR MIT) -""" -""" -import json -import subprocess -from os import environ -from sys import exit, argv -from pathlib import Path - -try: - (model_file,) = argv[1:] -except TypeError: - print("usage: {} inp.gdml".format(sys.argv[0])) - exit(2) - -exe = environ.get("CELERITAS_DEMO_EXE", "./demo-rasterizer") -ext = environ.get("CELER_TEST_EXT", "unknown") - -problem_name = "-".join([Path(model_file).stem, ext]) - -eps = 0.01 -inp = { - 'image': { - # TODO: input is cm for now; add 'units' argument? - 'lower_left': [-800, 0, -1500], - 'upper_right': [800, 0, 1600], - 'rightward': [1, 0, 0], - 'vertical_pixels': 128, - }, - 'input': model_file, - 'output': f"{problem_name}.bin" -} -print("Input:") -with open(f"{problem_name}.inp.json", 'w') as f: - json.dump(inp, f, indent=1) -print(json.dumps(inp, indent=1)) - -print("Running", exe) -result = subprocess.run([exe, '-'], - input=json.dumps(inp).encode(), - stdout=subprocess.PIPE) -if result.returncode: - print("Run failed with error", result.returncode) - exit(result.returncode) - -print("Received {} bytes of data".format(len(result.stdout))) -out_text = result.stdout.decode() -try: - result = json.loads(out_text) -except json.decoder.JSONDecodeError as e: - print("error: expected a JSON object but got the following stdout:") - print(out_text) - print("fatal:", str(e)) - exit(1) -print(json.dumps(result["metadata"], indent=1)) -with open(f'{problem_name}.out.json', 'w') as f: - json.dump(result, f) diff --git a/app/demo-rasterizer/visualize.py b/app/demo-rasterizer/visualize.py deleted file mode 100755 index 8482cbdb2a..0000000000 --- a/app/demo-rasterizer/visualize.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. -# See the top-level COPYRIGHT file for details. -# SPDX-License-Identifier: (Apache-2.0 OR MIT) -""" -""" -import json -import matplotlib.pyplot as plt -import numpy as np -import sys - -def read_image(input): - assert input['metadata']['int_size'] == 4 - image = np.fromfile(input['data'], dtype=np.int32) - return np.reshape(image, input['metadata']['dims']) - -def main(): - try: - (json_input, imgname) = sys.argv[1:] - except ValueError: - print("usage: {} [output.json] [image.png]".format(sys.argv[0])) - sys.exit(1) - if json_input != '-': - with open(json_input, 'r') as f: - input = json.load(f) - else: - input = json.load(sys.stdin) - - image = read_image(input) - - (fig, ax) = plt.subplots() - ax.imshow(image) - fig.savefig(imgname) - -if __name__ == '__main__': - main() diff --git a/doc/main/usage.rst b/doc/main/usage.rst index e3ba20f78e..9f3a521f83 100644 --- a/doc/main/usage.rst +++ b/doc/main/usage.rst @@ -186,6 +186,64 @@ Output The ROOT "MC truth" output file, if enabled with the command above, contains hits from all the sensitive detectors. + +.. _celer-geo: + +Visualization application (celer-geo) +===================================== + +The ``celer-geo`` app is a server-like front end to the Celeritas geometry +interfaces that can generate exact images of a user geometry model. + +Usage:: + + celer-geo {input}.jsonl + - + +Input +----- + +The input and output are both formatted as `JSON lines`_, a format where each +line (i.e., text ending with ``\\n``) is a valid JSON object. Each line of +input executes a command in ``celer-geo`` which will print to ``stdout`` a +single JSON line. Log messages are sent to ``stderr`` and can be +controlled by the :ref:`environment` variables. + +The first input command must define the input model (and may define additional +device settings):: + + {"geometry_file": "simple-cms.gdml"} + +Subsequent lines will each specify the imaging window, the geometry, the +binary image output filename, and the execution space (device or host for GPU +or CPU, respectively).:: + + {"image": {"_units": "cgs", "lower_left": [-800, 0, -1500], "upper_right": [800, 0, 1600], "rightward": [1, 0, 0], "vertical_pixels": 128}, "volumes": true, "bin_file": "simple-cms-cpu.orange.bin"} + +After the first image window is specified, it will be reused if the "image" key +is omitted. A new geometry and/or execution space may be specified, useful for +verifying different navigators behave identically:: + + {"bin_file": "simple-cms-cpu.geant4.bin", "geometry": "geant4"} + +An interrupt signal (``^C``), end-of-file (``^D``), or empty command will all +terminate the server. + +.. _JSON lines: https://jsonlines.org + +Output +------ + +If an input command is invalid or empty, an "example" (i.e., default but +incomplete input) will be output and the program may continue or be terminated. + +A successful raytrace will print the actually-used image parameters, geometry, +and execution space. If the "volumes" key was set to true, it will also +determine and print all the volume names for the geometry. + +When the server is directed to terminate, it will print diagnostic information +about the code, including timers about the geometry loading and tracing. + Additional utilities ==================== From a082c3298192af2b3d7cd4cdf72b195ae499c627 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Wed, 1 May 2024 13:31:31 -0500 Subject: [PATCH 33/59] Fix polar angle sampling and Mott factor in Wentzel distribution (#1212) * Update Wentzel distribution test results and use VEC_SOFT_EQ intead of SOFT_NEAR * Add lower energy point in Wentzel distribution test Fails with: ``` BernoulliDistribution.hh:69: celeritas: precondition failed: p_true >= 0 && p_true <= 1" ``` * Fix Mott factor and update tests * Fix sampling of cos theta and update test * Remove Coulomb scattering 100 MeV low energy limit and test more incident energies --- .../em/distribution/WentzelDistribution.hh | 8 +- .../interactor/CoulombScatteringInteractor.hh | 2 +- .../em/interactor/detail/PhysicsConstants.hh | 6 - .../em/model/CoulombScatteringModel.cc | 4 +- test/celeritas/em/CoulombScattering.test.cc | 136 +++++++++++------- 5 files changed, 92 insertions(+), 64 deletions(-) diff --git a/src/celeritas/em/distribution/WentzelDistribution.hh b/src/celeritas/em/distribution/WentzelDistribution.hh index 5ee6aadd16..fa9dcda109 100644 --- a/src/celeritas/em/distribution/WentzelDistribution.hh +++ b/src/celeritas/em/distribution/WentzelDistribution.hh @@ -147,7 +147,7 @@ CELER_FUNCTION real_type WentzelDistribution::operator()(Engine& rng) const // Calculate rejection for fake scattering // TODO: Reference? real_type mott_coeff - = 1 + real_type(1e-4) * ipow<2>(target_.atomic_number().get()); + = 1 + real_type(2e-4) * ipow<2>(target_.atomic_number().get()); MottRatioCalculator mott_xsec(element_data_, std::sqrt(particle_.beta_sq())); real_type g_rej = mott_xsec(cos_theta) @@ -262,9 +262,9 @@ CELER_FUNCTION real_type WentzelDistribution::sample_cos_t(real_type cos_t_max, real_type const xi = generate_canonical(rng); real_type const sc = helper_.screening_coefficient(); - return clamp(1 + 2 * sc * mu * (1 - xi) / (sc - mu * xi), - real_type{-1}, - real_type{1}); + real_type result = 1 - 2 * sc * mu * (1 - xi) / (sc + mu * xi); + CELER_ENSURE(result >= -1 && result <= 1); + return result; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/em/interactor/CoulombScatteringInteractor.hh b/src/celeritas/em/interactor/CoulombScatteringInteractor.hh index 54cd653f3f..6b7cc9d582 100644 --- a/src/celeritas/em/interactor/CoulombScatteringInteractor.hh +++ b/src/celeritas/em/interactor/CoulombScatteringInteractor.hh @@ -110,7 +110,7 @@ CoulombScatteringInteractor::CoulombScatteringInteractor( { CELER_EXPECT(particle_.particle_id() == shared.ids.electron || particle_.particle_id() == shared.ids.positron); - CELER_EXPECT(particle_.energy() > detail::coulomb_scattering_limit() + CELER_EXPECT(particle_.energy() > zero_quantity() && particle_.energy() < detail::high_energy_limit()); } diff --git a/src/celeritas/em/interactor/detail/PhysicsConstants.hh b/src/celeritas/em/interactor/detail/PhysicsConstants.hh index c47a2d8833..8160e0e0a9 100644 --- a/src/celeritas/em/interactor/detail/PhysicsConstants.hh +++ b/src/celeritas/em/interactor/detail/PhysicsConstants.hh @@ -64,12 +64,6 @@ CELER_CONSTEXPR_FUNCTION units::MevEnergy seltzer_berger_limit() return units::MevEnergy{1e3}; //! 1 GeV } -//! Minimum energy for Wentzel model to be applicable -CELER_CONSTEXPR_FUNCTION units::MevEnergy coulomb_scattering_limit() -{ - return units::MevEnergy{1e2}; //! 100 MeV -} - //! Maximum energy for EM models to be valid CELER_CONSTEXPR_FUNCTION units::MevEnergy high_energy_limit() { diff --git a/src/celeritas/em/model/CoulombScatteringModel.cc b/src/celeritas/em/model/CoulombScatteringModel.cc index 095d7b8982..59d2219072 100644 --- a/src/celeritas/em/model/CoulombScatteringModel.cc +++ b/src/celeritas/em/model/CoulombScatteringModel.cc @@ -81,7 +81,9 @@ auto CoulombScatteringModel::applicability() const -> SetApplicability { Applicability electron_applic; electron_applic.particle = this->host_ref().ids.electron; - electron_applic.lower = detail::coulomb_scattering_limit(); + // TODO: Set the lower energy limit equal to the MSC energy limit when + // combined single and multiple Coulomb scattering is supported and enabled + electron_applic.lower = zero_quantity(); electron_applic.upper = detail::high_energy_limit(); Applicability positron_applic = electron_applic; diff --git a/test/celeritas/em/CoulombScattering.test.cc b/test/celeritas/em/CoulombScattering.test.cc index 0ae3c37f63..6a75cf15be 100644 --- a/test/celeritas/em/CoulombScattering.test.cc +++ b/test/celeritas/em/CoulombScattering.test.cc @@ -281,28 +281,7 @@ TEST_F(CoulombScatteringTest, wokvi_transport_xs) TEST_F(CoulombScatteringTest, simple_scattering) { - int const num_samples = 10; - - static real_type const expected_angle[] = {1, - 0.99999999776622, - 0.99999999990987, - 0.99999999931707, - 0.99999999847986, - 0.9999999952274, - 0.99999999905465, - 0.99999999375773, - 1, - 0.99999999916491}; - static real_type const expected_energy[] = {200, - 199.99999999847, - 199.99999999994, - 199.99999999953, - 199.99999999896, - 199.99999999673, - 199.99999999935, - 199.99999999572, - 200, - 199.99999999943}; + int const num_samples = 4; IsotopeView const isotope = this->material_track() .make_material_view() @@ -310,46 +289,93 @@ TEST_F(CoulombScatteringTest, simple_scattering) .make_isotope_view(IsotopeComponentId{0}); auto cutoffs = this->cutoff_params()->get(MaterialId{0}); - CoulombScatteringInteractor interact(model_->host_ref(), - this->particle_track(), - this->direction(), - isotope, - ElementId{0}, - cutoffs); RandomEngine& rng_engine = this->rng(); - std::vector angle; - std::vector energy; + std::vector cos_theta; + std::vector delta_energy; - for ([[maybe_unused]] int i : range(num_samples)) + std::vector energies{0.2, 1, 10, 100, 1000, 100000}; + for (auto energy : energies) { - Interaction result = interact(rng_engine); - SCOPED_TRACE(result); - this->sanity_check(result); + this->set_inc_particle(pdg::electron(), MevEnergy{energy}); + CoulombScatteringInteractor interact(model_->host_ref(), + this->particle_track(), + this->direction(), + isotope, + ElementId{0}, + cutoffs); - energy.push_back(result.energy.value()); - angle.push_back(dot_product(this->direction(), result.direction)); - } + for ([[maybe_unused]] int i : range(num_samples)) + { + Interaction result = interact(rng_engine); + SCOPED_TRACE(result); + this->sanity_check(result); - EXPECT_VEC_SOFT_EQ(expected_angle, angle); - EXPECT_VEC_SOFT_EQ(expected_energy, energy); + cos_theta.push_back( + dot_product(this->direction(), result.direction)); + delta_energy.push_back(energy - result.energy.value()); + } + } + static double const expected_cos_theta[] = {1, + 0.9996601312603, + 0.99998628518524, + 0.9998960794451, + 1, + 0.99991152273528, + 0.99998247385882, + 0.99988427878015, + 1, + 0.99999970191006, + 0.99999637934855, + 0.99999885954183, + 0.999999997443, + 0.99999999406847, + 0.99999999849197, + 1, + 0.99999999992169, + 1, + 0.99999999209019, + 0.99999999999489, + 1, + 0.99999999999999, + 0.99999999999999, + 1}; + static double const expected_delta_energy[] = {0, + 1.4170232209842e-09, + 5.7181509527382e-11, + 4.3327857968123e-10, + 0, + 3.0519519134131e-09, + 6.0455007666604e-10, + 3.9917101846143e-09, + 0, + 5.6049742624964e-10, + 6.8078875870015e-09, + 2.1443966602419e-09, + 4.406643938637e-10, + 1.0222294122286e-09, + 2.5988811103161e-10, + 0, + 1.3371845852816e-09, + 0, + 1.350750835627e-07, + 8.7311491370201e-11, + 4.8021320253611e-10, + 8.7311491370201e-10, + 1.6734702512622e-09, + 0}; + EXPECT_VEC_SOFT_EQ(expected_cos_theta, cos_theta); + EXPECT_VEC_SOFT_EQ(expected_delta_energy, delta_energy); } TEST_F(CoulombScatteringTest, distribution) { CoulombScatteringHostRef const& data = model_->host_ref(); + std::vector avg_angles; - std::vector const energies = {50, 100, 200, 1000, 13000}; - - static real_type const expected_avg_angles[] = {0.99999962180819, - 0.99999973999034, - 0.9999999728531, - 0.99999999909264, - 0.99999999999393}; - - for (size_t i : range(energies.size())) + for (real_type energy : {1, 50, 100, 200, 1000, 13000}) { - this->set_inc_particle(pdg::electron(), MevEnergy{energies[i]}); + this->set_inc_particle(pdg::electron(), MevEnergy{energy}); CoulombScatteringElementData const& element_data = data.elem_data[ElementId(0)]; @@ -378,10 +404,16 @@ TEST_F(CoulombScatteringTest, distribution) } avg_angle /= num_samples; - - EXPECT_SOFT_NEAR( - expected_avg_angles[i], avg_angle, std::sqrt(num_samples)); + avg_angles.push_back(avg_angle); } + + static double const expected_avg_angles[] = {0.99933909299229, + 0.99999960697043, + 0.99999986035881, + 0.99999998052954, + 0.99999999917037, + 0.9999999999969}; + EXPECT_VEC_SOFT_EQ(expected_avg_angles, avg_angles); } //---------------------------------------------------------------------------// From 15283390c12ddaec1722518bda2ef4ab3e95e3a6 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 1 May 2024 16:28:33 -0400 Subject: [PATCH 34/59] Release v0.4.3 (#1216) * Update documentation * Only write 'aborted' count at end * Update frontier toolchain location --- app/celer-sim/Transporter.cc | 7 ++--- doc/appendix/release-history/v0.4.rst | 44 +++++++++++++++++++++++++++ scripts/cmake-presets/frontier.json | 3 +- scripts/env/frontier.sh | 13 ++++---- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index d193409c63..e34538158a 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -92,7 +92,6 @@ auto Transporter::operator()(SpanConstPrimary primaries) } ++result.num_step_iterations; result.num_steps += track_counts.active; - result.num_aborted = track_counts.alive + track_counts.queued; result.max_queued = std::max(result.max_queued, track_counts.queued); }; @@ -136,9 +135,8 @@ auto Transporter::operator()(SpanConstPrimary primaries) } if (CELER_UNLIKELY(interrupted())) { - CELER_LOG_LOCAL(error) - << "Caught interrupt signal: aborting transport " - "loop"; + CELER_LOG_LOCAL(error) << "Caught interrupt signal: aborting " + "transport loop"; interrupted = {}; break; } @@ -153,6 +151,7 @@ auto Transporter::operator()(SpanConstPrimary primaries) } } + result.num_aborted = track_counts.alive + track_counts.queued; result.num_track_slots = stepper_->state().size(); return result; } diff --git a/doc/appendix/release-history/v0.4.rst b/doc/appendix/release-history/v0.4.rst index a3f3b48e84..a7ca0dbf4c 100644 --- a/doc/appendix/release-history/v0.4.rst +++ b/doc/appendix/release-history/v0.4.rst @@ -3,6 +3,50 @@ .. SPDX-License-Identifier: CC-BY-4.0 +.. _release_v0.4.3: + +Version 0.4.3 +============= + +*Released 2024/05/01* + +Version 0.4.3 is a bugfix and minor feature update to Celeritas. + +- Adds diagnostic output for ``celer-sim`` even when not recording all steps +- Fixes a sampling error for keV electron ionization +- Builds with ROCm 5.7.1 +- Builds with Geant4 10.5–10.7, 11.1.0 + + +New features +------------ + +* Add JSON test comparator to improve testing reliability *(@sethrj, #1115)* +* Add spack-based workflow to add all supported Geant4 versions to CI *(@sethrj, #1149)* +* Add always-on basic diagnostics to celer-sim *(@amandalund, #1214)* + +Reviewers: @amandalund *(1)*, @drbenmorgan *(1)*, @sethrj *(1)* + +Bug fixes +--------- + +* Fix Moller-Bhahba energy distribution *(@amandalund, #1138)* +* Fix Geant4 build from 10.5–10.7 and example build for 11.1 onward *(@sethrj, #1152)* +* Fix missing Werror in build-fast workflow *(@sethrj, #1141)* +* Fix build error with Geant4@11.1.0 *(@amandalund, #1199)* + +Reviewers: @sethrj *(2)*, @whokion *(1)*, @drbenmorgan *(1)*, @amandalund *(1)* + +Documentation improvements +-------------------------- + +* Update Frontier installation *(@sethrj, #1208)* + +Reviewers: @esseivaju *(1)* + +**Full Changelog**: https://github.com/celeritas-project/celeritas/compare/v0.4.2...v0.4.3 + + .. _release_v0.4.2: Version 0.4.2 diff --git a/scripts/cmake-presets/frontier.json b/scripts/cmake-presets/frontier.json index f1b72dfa9f..2f4f5afa5b 100644 --- a/scripts/cmake-presets/frontier.json +++ b/scripts/cmake-presets/frontier.json @@ -63,7 +63,8 @@ "displayName": "Frontier release mode", "inherits": [".ndebug", ".base"], "cacheVariables": { - "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "OFF"} + "BUILD_SHARED_LIBS": {"type": "BOOL", "value": "OFF"}, + "CMAKE_INSTALL_PREFIX": "/lustre/orion/world-shared/hep143/celeritas/develop" } }, { diff --git a/scripts/env/frontier.sh b/scripts/env/frontier.sh index 6108149b04..07e614fa0b 100755 --- a/scripts/env/frontier.sh +++ b/scripts/env/frontier.sh @@ -1,9 +1,8 @@ #!/bin/sh -e PROJID=hep143 -_celer_view=${PROJWORK}/${PROJID}/opt-view -_tool_view=/ccs/proj/${PROJID}/opt-view -_conda=/ccs/proj/${PROJID}/conda-frontier +_worldwork=${WORLDWORK}/${PROJID} +_ccsproj=/ccs/proj/${PROJID} module load PrgEnv-amd/8.5.0 cpe/23.12 amd/5.7.1 craype-x86-trento \ libfabric/1.15.2.0 miniforge3/23.11.0 @@ -27,10 +26,10 @@ export CC=${CRAYPE_DIR}/bin/cc # module load craype-accel-amd-gfx90a # Set up celeritas -export SPACK_ROOT=/ccs/proj/hep143/spack -export PATH=${_celer_view}/bin:${_tool_view}/bin:${_conda}/bin:$PATH -export CMAKE_PREFIX_PATH=${_celer_view}:${CMAKE_PREFIX_PATH} -export MODULEPATH=${PROJWORK}/${PROJID}/share/lmod/linux-sles15-x86_64/Core:${MODULEPATH} +export SPACK_ROOT=${_ccsproj}/spack +export PATH=${_worldwork}/opt-view/bin:${_ccsproj}/opt-view/bin:${_ccsproj}/conda-frontier/bin:$PATH +export CMAKE_PREFIX_PATH=${_worldwork}/opt-view:${CMAKE_PREFIX_PATH} +export MODULEPATH=${_worldwork}/share/lmod/linux-sles15-x86_64/Core:${MODULEPATH} # Set up Geant4 data module load geant4-data/11.0 From cfd1e83b7bc2311f2a0da0a71b2d58284d7c0c13 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Fri, 3 May 2024 11:22:34 -0500 Subject: [PATCH 35/59] Split optical pre-generator action into separate Cerenkov and scintillation actions (#1217) --- src/celeritas/CMakeLists.txt | 4 +- src/celeritas/optical/OpticalCollector.cc | 27 ++- src/celeritas/optical/OpticalCollector.hh | 10 +- src/celeritas/optical/OpticalGenData.hh | 5 + .../optical/detail/CerenkovPreGenAction.cc | 132 ++++++++++++++ .../optical/detail/CerenkovPreGenAction.cu | 48 +++++ ...reGenAction.hh => CerenkovPreGenAction.hh} | 46 +---- ...nExecutor.hh => CerenkovPreGenExecutor.hh} | 44 ++--- .../optical/detail/OpticalGenAlgorithms.cc | 37 ++++ .../optical/detail/OpticalGenAlgorithms.cu | 47 +++++ .../optical/detail/OpticalGenAlgorithms.hh | 59 ++++++ src/celeritas/optical/detail/PreGenAction.cc | 170 ------------------ src/celeritas/optical/detail/PreGenAction.cu | 74 -------- .../optical/detail/PreGenGatherAction.hh | 2 +- .../optical/detail/ScintPreGenAction.cc | 128 +++++++++++++ .../optical/detail/ScintPreGenAction.cu | 46 +++++ .../optical/detail/ScintPreGenAction.hh | 82 +++++++++ .../optical/detail/ScintPreGenExecutor.hh | 80 +++++++++ src/celeritas/optical/detail/Utils.hh | 29 +++ .../optical/OpticalCollector.test.cc | 96 +++++++--- 20 files changed, 822 insertions(+), 344 deletions(-) create mode 100644 src/celeritas/optical/detail/CerenkovPreGenAction.cc create mode 100644 src/celeritas/optical/detail/CerenkovPreGenAction.cu rename src/celeritas/optical/detail/{PreGenAction.hh => CerenkovPreGenAction.hh} (62%) rename src/celeritas/optical/detail/{PreGenExecutor.hh => CerenkovPreGenExecutor.hh} (65%) create mode 100644 src/celeritas/optical/detail/OpticalGenAlgorithms.cc create mode 100644 src/celeritas/optical/detail/OpticalGenAlgorithms.cu create mode 100644 src/celeritas/optical/detail/OpticalGenAlgorithms.hh delete mode 100644 src/celeritas/optical/detail/PreGenAction.cc delete mode 100644 src/celeritas/optical/detail/PreGenAction.cu create mode 100644 src/celeritas/optical/detail/ScintPreGenAction.cc create mode 100644 src/celeritas/optical/detail/ScintPreGenAction.cu create mode 100644 src/celeritas/optical/detail/ScintPreGenAction.hh create mode 100644 src/celeritas/optical/detail/ScintPreGenExecutor.hh create mode 100644 src/celeritas/optical/detail/Utils.hh diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 3ff4bdfffb..509743bc3f 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -256,8 +256,10 @@ celeritas_polysource(global/alongstep/AlongStepUniformMscAction) celeritas_polysource(global/alongstep/AlongStepRZMapFieldMscAction) celeritas_polysource(neutron/model/ChipsNeutronElasticModel) celeritas_polysource(neutron/model/NeutronInelasticModel) -celeritas_polysource(optical/detail/PreGenAction) +celeritas_polysource(optical/detail/CerenkovPreGenAction) +celeritas_polysource(optical/detail/OpticalGenAlgorithms) celeritas_polysource(optical/detail/PreGenGatherAction) +celeritas_polysource(optical/detail/ScintPreGenAction) celeritas_polysource(phys/detail/DiscreteSelectAction) celeritas_polysource(phys/detail/PreStepAction) celeritas_polysource(random/RngReseed) diff --git a/src/celeritas/optical/OpticalCollector.cc b/src/celeritas/optical/OpticalCollector.cc index 0b68e3b9d1..650cd93067 100644 --- a/src/celeritas/optical/OpticalCollector.cc +++ b/src/celeritas/optical/OpticalCollector.cc @@ -39,14 +39,25 @@ OpticalCollector::OpticalCollector(Input inp) inp.action_registry->next_id(), storage_); inp.action_registry->insert(gather_action_); - // Action to generate Cerenkov and scintillation optical distributions - pregen_action_ = std::make_shared( - inp.action_registry->next_id(), - inp.properties, - inp.cerenkov, - inp.scintillation, - storage_); - inp.action_registry->insert(pregen_action_); + if (host_data.cerenkov) + { + // Action to generate Cerenkov optical distributions + cerenkov_pregen_action_ + = std::make_shared( + inp.action_registry->next_id(), + inp.properties, + inp.cerenkov, + storage_); + inp.action_registry->insert(cerenkov_pregen_action_); + } + + if (host_data.scintillation) + { + // Action to generate scintillation optical distributions + scint_pregen_action_ = std::make_shared( + inp.action_registry->next_id(), inp.scintillation, storage_); + inp.action_registry->insert(scint_pregen_action_); + } // TODO: add an action to launch optical tracking loop } diff --git a/src/celeritas/optical/OpticalCollector.hh b/src/celeritas/optical/OpticalCollector.hh index dde9b20eda..5a1ed3cb1a 100644 --- a/src/celeritas/optical/OpticalCollector.hh +++ b/src/celeritas/optical/OpticalCollector.hh @@ -12,8 +12,9 @@ #include "celeritas/Types.hh" #include "celeritas/optical/OpticalGenData.hh" -#include "detail/PreGenAction.hh" +#include "detail/CerenkovPreGenAction.hh" #include "detail/PreGenGatherAction.hh" +#include "detail/ScintPreGenAction.hh" namespace celeritas { @@ -84,14 +85,17 @@ class OpticalCollector private: //// TYPES //// - using SPPreGenAction = std::shared_ptr; + using SPCerenkovPreGenAction + = std::shared_ptr; + using SPScintPreGenAction = std::shared_ptr; using SPGatherAction = std::shared_ptr; //// DATA //// SPGenStorage storage_; SPGatherAction gather_action_; - SPPreGenAction pregen_action_; + SPCerenkovPreGenAction cerenkov_pregen_action_; + SPScintPreGenAction scint_pregen_action_; // TODO: tracking loop launcher // TODO: store optical core params and state? }; diff --git a/src/celeritas/optical/OpticalGenData.hh b/src/celeritas/optical/OpticalGenData.hh index 0e50eee316..1661bcf680 100644 --- a/src/celeritas/optical/OpticalGenData.hh +++ b/src/celeritas/optical/OpticalGenData.hh @@ -84,6 +84,11 @@ struct OpticalPreStepData //---------------------------------------------------------------------------// /*! * Optical photon distribution data. + * + * The distributions are stored in separate Cerenkov and scintillation buffers + * indexed by the current buffer size plus the track slot ID. The data is + * compacted at the end of each step by removing all invalid distributions. The + * order of the distributions in the buffers is guaranteed to be reproducible. */ template struct OpticalGenStateData diff --git a/src/celeritas/optical/detail/CerenkovPreGenAction.cc b/src/celeritas/optical/detail/CerenkovPreGenAction.cc new file mode 100644 index 0000000000..b34aa97589 --- /dev/null +++ b/src/celeritas/optical/detail/CerenkovPreGenAction.cc @@ -0,0 +1,132 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/CerenkovPreGenAction.cc +//---------------------------------------------------------------------------// +#include "CerenkovPreGenAction.hh" + +#include + +#include "corecel/Assert.hh" +#include "celeritas/global/ActionLauncher.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/CoreTrackData.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/optical/CerenkovParams.hh" +#include "celeritas/optical/OpticalPropertyParams.hh" + +#include "CerenkovPreGenExecutor.hh" +#include "OpticalGenAlgorithms.hh" +#include "OpticalGenStorage.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with action ID, optical properties, and storage. + */ +CerenkovPreGenAction::CerenkovPreGenAction(ActionId id, + SPConstProperties properties, + SPConstCerenkov cerenkov, + SPGenStorage storage) + : id_(id) + , properties_(std::move(properties)) + , cerenkov_(std::move(cerenkov)) + , storage_(std::move(storage)) +{ + CELER_EXPECT(id_); + CELER_EXPECT(cerenkov_ && properties_); + CELER_EXPECT(storage_); +} + +//---------------------------------------------------------------------------// +/*! + * Descriptive name of the action. + */ +std::string_view CerenkovPreGenAction::description() const +{ + return "generate Cerenkov optical distribution data"; +} + +//---------------------------------------------------------------------------// +/*! + * Execute the action with host data. + */ +void CerenkovPreGenAction::execute(CoreParams const& params, + CoreStateHost& state) const +{ + this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Execute the action with device data. + */ +void CerenkovPreGenAction::execute(CoreParams const& params, + CoreStateDevice& state) const +{ + this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data post-step. + */ +template +void CerenkovPreGenAction::execute_impl(CoreParams const& core_params, + CoreState& core_state) const +{ + size_type state_size = core_state.size(); + StreamId stream = core_state.stream_id(); + auto& buffer_size = storage_->size[stream.get()]; + auto const& state = storage_->obj.state(stream, state_size); + + CELER_VALIDATE(buffer_size.cerenkov + state_size <= state.cerenkov.size(), + << "insufficient capacity (" << state.cerenkov.size() + << ") for buffered Cerenkov distribution data (total " + "capacity requirement of " + << buffer_size.cerenkov + state_size << ")"); + + // Generate the optical distribution data + this->pre_generate(core_params, core_state); + + // Compact the buffer + buffer_size.cerenkov = remove_if_invalid( + state.cerenkov, buffer_size.cerenkov, state_size, stream); +} + +//---------------------------------------------------------------------------// +/*! + * Launch a (host) kernel to generate optical distribution data post-step. + */ +void CerenkovPreGenAction::pre_generate(CoreParams const& core_params, + CoreStateHost& core_state) const +{ + TrackExecutor execute{core_params.ptr(), + core_state.ptr(), + detail::CerenkovPreGenExecutor{ + properties_->host_ref(), + cerenkov_->host_ref(), + storage_->obj.state( + core_state.stream_id(), core_state.size()), + storage_->size[core_state.stream_id().get()]}}; + launch_action(*this, core_params, core_state, execute); +} + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +void CerenkovPreGenAction::pre_generate(CoreParams const&, + CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/CerenkovPreGenAction.cu b/src/celeritas/optical/detail/CerenkovPreGenAction.cu new file mode 100644 index 0000000000..5165ce6bd0 --- /dev/null +++ b/src/celeritas/optical/detail/CerenkovPreGenAction.cu @@ -0,0 +1,48 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/CerenkovPreGenAction.cu +//---------------------------------------------------------------------------// +#include "CerenkovPreGenAction.hh" + +#include "corecel/Assert.hh" +#include "corecel/sys/ScopedProfiling.hh" +#include "celeritas/global/ActionLauncher.device.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/optical/CerenkovParams.hh" +#include "celeritas/optical/OpticalPropertyParams.hh" + +#include "CerenkovPreGenExecutor.hh" +#include "OpticalGenAlgorithms.hh" +#include "OpticalGenStorage.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Launch a kernel to generate optical distribution data post-step. + */ +void CerenkovPreGenAction::pre_generate(CoreParams const& core_params, + CoreStateDevice& core_state) const +{ + TrackExecutor execute{core_params.ptr(), + core_state.ptr(), + detail::CerenkovPreGenExecutor{ + properties_->device_ref(), + cerenkov_->device_ref(), + storage_->obj.state( + core_state.stream_id(), core_state.size()), + storage_->size[core_state.stream_id().get()]}}; + static ActionLauncher const launch_kernel(*this); + launch_kernel(core_state, execute); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenAction.hh b/src/celeritas/optical/detail/CerenkovPreGenAction.hh similarity index 62% rename from src/celeritas/optical/detail/PreGenAction.hh rename to src/celeritas/optical/detail/CerenkovPreGenAction.hh index 7350dfe0ba..16ba6ef676 100644 --- a/src/celeritas/optical/detail/PreGenAction.hh +++ b/src/celeritas/optical/detail/CerenkovPreGenAction.hh @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/optical/detail/PreGenAction.hh +//! \file celeritas/optical/detail/CerenkovPreGenAction.hh //---------------------------------------------------------------------------// #pragma once @@ -18,7 +18,6 @@ namespace celeritas { class CerenkovParams; class OpticalPropertyParams; -class ScintillationParams; namespace detail { @@ -27,34 +26,22 @@ struct OpticalGenStorage; /*! * Generate optical distribution data. */ -class PreGenAction final : public ExplicitCoreActionInterface +class CerenkovPreGenAction final : public ExplicitCoreActionInterface { public: //!@{ //! \name Type aliases using SPConstCerenkov = std::shared_ptr; using SPConstProperties = std::shared_ptr; - using SPConstScintillation = std::shared_ptr; using SPGenStorage = std::shared_ptr; //!@} - //! Check if the distribution data is valid - struct IsInvalid - { - CELER_FUNCTION bool - operator()(OpticalDistributionData const& data) const - { - return !data; - } - }; - public: // Construct with action ID, optical properties, and storage - PreGenAction(ActionId id, - SPConstProperties properties, - SPConstCerenkov cerenkov, - SPConstScintillation scintillation, - SPGenStorage storage); + CerenkovPreGenAction(ActionId id, + SPConstProperties properties, + SPConstCerenkov cerenkov, + SPGenStorage storage); // Launch kernel with host data void execute(CoreParams const&, CoreStateHost&) const final; @@ -66,10 +53,7 @@ class PreGenAction final : public ExplicitCoreActionInterface ActionId action_id() const final { return id_; } //! Short name for the action - std::string_view label() const final - { - return "optical-pre-generator-post"; - } + std::string_view label() const final { return "cerenkov-pre-generator"; } // Name of the action (for user output) std::string_view description() const final; @@ -78,18 +62,11 @@ class PreGenAction final : public ExplicitCoreActionInterface ActionOrder order() const final { return ActionOrder::post_post; } private: - //// TYPES //// - - template - using ItemsRef - = Collection; - //// DATA //// ActionId id_; SPConstProperties properties_; SPConstCerenkov cerenkov_; - SPConstScintillation scintillation_; SPGenStorage storage_; //// HELPER FUNCTIONS //// @@ -99,15 +76,6 @@ class PreGenAction final : public ExplicitCoreActionInterface void pre_generate(CoreParams const&, CoreStateHost&) const; void pre_generate(CoreParams const&, CoreStateDevice&) const; - - size_type remove_if_invalid(ItemsRef const&, - size_type, - size_type, - StreamId) const; - size_type remove_if_invalid(ItemsRef const&, - size_type, - size_type, - StreamId) const; }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/optical/detail/PreGenExecutor.hh b/src/celeritas/optical/detail/CerenkovPreGenExecutor.hh similarity index 65% rename from src/celeritas/optical/detail/PreGenExecutor.hh rename to src/celeritas/optical/detail/CerenkovPreGenExecutor.hh index 75423429c7..24e1ad34f9 100644 --- a/src/celeritas/optical/detail/PreGenExecutor.hh +++ b/src/celeritas/optical/detail/CerenkovPreGenExecutor.hh @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/optical/detail/PreGenExecutor.hh +//! \file celeritas/optical/detail/CerenkovPreGenExecutor.hh //---------------------------------------------------------------------------// #pragma once @@ -12,7 +12,8 @@ #include "celeritas/global/CoreTrackView.hh" #include "celeritas/optical/CerenkovPreGenerator.hh" #include "celeritas/optical/OpticalGenData.hh" -#include "celeritas/optical/ScintillationPreGenerator.hh" + +#include "Utils.hh" namespace celeritas { @@ -24,14 +25,13 @@ namespace detail /*! * Generate optical distribution data. */ -struct PreGenExecutor +struct CerenkovPreGenExecutor { inline CELER_FUNCTION void operator()(celeritas::CoreTrackView const& track); NativeCRef const properties; NativeCRef const cerenkov; - NativeCRef const scintillation; NativeRef const state; OpticalBufferSize size; }; @@ -42,44 +42,39 @@ struct PreGenExecutor /*! * Generate optical distribution data. */ -CELER_FUNCTION void PreGenExecutor::operator()(CoreTrackView const& track) +CELER_FUNCTION void +CerenkovPreGenExecutor::operator()(CoreTrackView const& track) { CELER_EXPECT(state); + CELER_EXPECT(cerenkov); + CELER_EXPECT(properties); using DistId = ItemId; auto tsid = track.track_slot_id(); + CELER_ASSERT(size.cerenkov + tsid.get() < state.cerenkov.size()); auto& cerenkov_dist = state.cerenkov[DistId(size.cerenkov + tsid.get())]; - auto& scintillation_dist - = state.scintillation[DistId(size.scintillation + tsid.get())]; // Clear distribution data cerenkov_dist = {}; - scintillation_dist = {}; auto sim = track.make_sim_view(); - bool inactive = sim.status() == TrackStatus::inactive; - auto optmat_id = inactive ? OpticalMaterialId{} - : track.make_material_view() - .make_material_view() - .optical_material_id(); - - if (inactive || !optmat_id || sim.step_length() == 0) + auto optmat_id = get_optical_material(track); + if (!optmat_id || sim.step_length() == 0) { // Inactive tracks, materials with no optical properties, or particles // that started the step with zero energy (e.g. a stopped positron) return; } - Real3 const& pos = track.make_geo_view().pos(); auto particle = track.make_particle_view(); - auto rng = track.make_rng_engine(); - // Get the distribution data used to generate scintillation and Cerenkov - // optical photons - if (cerenkov && particle.charge() != zero_quantity()) + // Get the distribution data used to generate Cerenkov optical photons + if (particle.charge() != zero_quantity()) { - CELER_ASSERT(properties); + Real3 const& pos = track.make_geo_view().pos(); + auto rng = track.make_rng_engine(); + CerenkovPreGenerator generate(particle, sim, pos, @@ -89,13 +84,6 @@ CELER_FUNCTION void PreGenExecutor::operator()(CoreTrackView const& track) state.step[tsid]); cerenkov_dist = generate(rng); } - if (scintillation) - { - auto edep = track.make_physics_step_view().energy_deposition(); - ScintillationPreGenerator generate( - particle, sim, pos, optmat_id, edep, scintillation, state.step[tsid]); - scintillation_dist = generate(rng); - } } //---------------------------------------------------------------------------// diff --git a/src/celeritas/optical/detail/OpticalGenAlgorithms.cc b/src/celeritas/optical/detail/OpticalGenAlgorithms.cc new file mode 100644 index 0000000000..a7d963ea49 --- /dev/null +++ b/src/celeritas/optical/detail/OpticalGenAlgorithms.cc @@ -0,0 +1,37 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/track/detail/OpticalGenAlgorithms.cc +//---------------------------------------------------------------------------// +#include "OpticalGenAlgorithms.hh" + +#include + +#include "corecel/Assert.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Remove all invalid distributions from the buffer. + */ +size_type remove_if_invalid(Collection const& buffer, + size_type offset, + size_type size, + StreamId) +{ + auto* start = static_cast(buffer.data()); + auto* stop + = std::remove_if(start + offset, start + offset + size, IsInvalid{}); + return stop - start; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/OpticalGenAlgorithms.cu b/src/celeritas/optical/detail/OpticalGenAlgorithms.cu new file mode 100644 index 0000000000..cf7ebc543d --- /dev/null +++ b/src/celeritas/optical/detail/OpticalGenAlgorithms.cu @@ -0,0 +1,47 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/track/detail/OpticalGenAlgorithms.cu +//---------------------------------------------------------------------------// +#include "OpticalGenAlgorithms.hh" + +#include +#include +#include + +#include "corecel/Assert.hh" +#include "corecel/Macros.hh" +#include "corecel/sys/Device.hh" +#include "corecel/sys/ScopedProfiling.hh" +#include "corecel/sys/Thrust.device.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Remove all invalid distributions from the buffer. + */ +size_type remove_if_invalid(Collection const& buffer, + size_type offset, + size_type size, + StreamId stream) +{ + ScopedProfiling profile_this{"remove-if-invalid"}; + auto start = thrust::device_pointer_cast(buffer.data().get()); + auto stop = thrust::remove_if(thrust_execute_on(stream), + start + offset, + start + offset + size, + IsInvalid{}); + CELER_DEVICE_CHECK_ERROR(); + return stop - start; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/OpticalGenAlgorithms.hh b/src/celeritas/optical/detail/OpticalGenAlgorithms.hh new file mode 100644 index 0000000000..79a3e00932 --- /dev/null +++ b/src/celeritas/optical/detail/OpticalGenAlgorithms.hh @@ -0,0 +1,59 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/OpticalGenAlgorithms.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/optical/OpticalDistributionData.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +struct IsInvalid +{ + // Check if the distribution data is valid + CELER_FUNCTION bool operator()(OpticalDistributionData const& data) const + { + return !data; + } +}; + +//---------------------------------------------------------------------------// +// Remove all invalid distributions from the buffer. +size_type remove_if_invalid( + Collection const&, + size_type, + size_type, + StreamId); +size_type remove_if_invalid(Collection const&, + size_type, + size_type, + StreamId); + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +inline size_type remove_if_invalid(Collection const&, + size_type, + size_type, + StreamId) +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenAction.cc b/src/celeritas/optical/detail/PreGenAction.cc deleted file mode 100644 index 59338d4dc6..0000000000 --- a/src/celeritas/optical/detail/PreGenAction.cc +++ /dev/null @@ -1,170 +0,0 @@ -//----------------------------------*-C++-*----------------------------------// -// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file celeritas/optical/detail/PreGenAction.cc -//---------------------------------------------------------------------------// -#include "PreGenAction.hh" - -#include - -#include "corecel/Assert.hh" -#include "celeritas/global/ActionLauncher.hh" -#include "celeritas/global/CoreParams.hh" -#include "celeritas/global/CoreState.hh" -#include "celeritas/global/CoreTrackData.hh" -#include "celeritas/global/TrackExecutor.hh" -#include "celeritas/optical/CerenkovParams.hh" -#include "celeritas/optical/OpticalPropertyParams.hh" -#include "celeritas/optical/ScintillationParams.hh" - -#include "OpticalGenStorage.hh" -#include "PreGenExecutor.hh" - -namespace celeritas -{ -namespace detail -{ -//---------------------------------------------------------------------------// -/*! - * Construct with action ID, optical properties, and storage. - */ -PreGenAction::PreGenAction(ActionId id, - SPConstProperties properties, - SPConstCerenkov cerenkov, - SPConstScintillation scintillation, - SPGenStorage storage) - : id_(id) - , properties_(std::move(properties)) - , cerenkov_(std::move(cerenkov)) - , scintillation_(std::move(scintillation)) - , storage_(std::move(storage)) -{ - CELER_EXPECT(id_); - CELER_EXPECT(scintillation_ || (cerenkov_ && properties_)); - CELER_EXPECT(storage_); -} - -//---------------------------------------------------------------------------// -/*! - * Descriptive name of the action. - */ -std::string_view PreGenAction::description() const -{ - return "generate Cerenkov and scintillation optical distribution data"; -} - -//---------------------------------------------------------------------------// -/*! - * Execute the action with host data. - */ -void PreGenAction::execute(CoreParams const& params, CoreStateHost& state) const -{ - this->execute_impl(params, state); -} - -//---------------------------------------------------------------------------// -/*! - * Execute the action with device data. - */ -void PreGenAction::execute(CoreParams const& params, - CoreStateDevice& state) const -{ - this->execute_impl(params, state); -} - -//---------------------------------------------------------------------------// -/*! - * Generate optical distribution data post-step. - * - * The distributions are stored in separate Cerenkov and scintillation buffers - * indexed by the current buffer size plus the track slot ID. The data is - * compacted at the end of each step by removing all invalid distributions. The - * order of the distributions in the buffers is guaranteed to be reproducible. - */ -template -void PreGenAction::execute_impl(CoreParams const& core_params, - CoreState& core_state) const -{ - size_type state_size = core_state.size(); - StreamId stream = core_state.stream_id(); - auto& buffer_size = storage_->size[stream.get()]; - auto const& state = storage_->obj.state(stream, state_size); - - CELER_VALIDATE(buffer_size.cerenkov + state_size <= state.cerenkov.size(), - << "insufficient capacity (" << state.cerenkov.size() - << ") for buffered Cerenkov distribution data (total " - "capacity requirement of " - << buffer_size.cerenkov + state_size << ")"); - CELER_VALIDATE( - buffer_size.scintillation + state_size <= state.scintillation.size(), - << "insufficient capacity (" << state.scintillation.size() - << ") for buffered scintillation distribution data (total " - "capacity requirement of " - << buffer_size.scintillation + state_size << ")"); - - // Generate the optical distribution data - this->pre_generate(core_params, core_state); - - // Compact the buffers - buffer_size.cerenkov = this->remove_if_invalid( - state.cerenkov, buffer_size.cerenkov, state_size, stream); - buffer_size.scintillation = this->remove_if_invalid( - state.scintillation, buffer_size.scintillation, state_size, stream); -} - -//---------------------------------------------------------------------------// -/*! - * Launch a (host) kernel to generate optical distribution data post-step. - */ -void PreGenAction::pre_generate(CoreParams const& core_params, - CoreStateHost& core_state) const -{ - TrackExecutor execute{ - core_params.ptr(), - core_state.ptr(), - detail::PreGenExecutor{properties_->host_ref(), - cerenkov_->host_ref(), - scintillation_->host_ref(), - storage_->obj.state( - core_state.stream_id(), core_state.size()), - storage_->size[core_state.stream_id().get()]}}; - launch_action(*this, core_params, core_state, execute); -} - -//---------------------------------------------------------------------------// -/*! - * Remove all invalid distributions from the buffer. - */ -size_type -PreGenAction::remove_if_invalid(ItemsRef const& buffer, - size_type offset, - size_type size, - StreamId) const -{ - auto* start = static_cast(buffer.data()); - auto* stop - = std::remove_if(start + offset, start + offset + size, IsInvalid{}); - return stop - start; -} - -//---------------------------------------------------------------------------// -#if !CELER_USE_DEVICE -void PreGenAction::pre_generate(CoreParams const&, CoreStateDevice&) const -{ - CELER_NOT_CONFIGURED("CUDA OR HIP"); -} - -size_type PreGenAction::remove_if_invalid(ItemsRef const&, - size_type, - size_type, - StreamId) const -{ - CELER_NOT_CONFIGURED("CUDA OR HIP"); -} -#endif - -//---------------------------------------------------------------------------// -} // namespace detail -} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenAction.cu b/src/celeritas/optical/detail/PreGenAction.cu deleted file mode 100644 index 7fe42b6ffb..0000000000 --- a/src/celeritas/optical/detail/PreGenAction.cu +++ /dev/null @@ -1,74 +0,0 @@ -//---------------------------------*-CUDA-*----------------------------------// -// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. -// See the top-level COPYRIGHT file for details. -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file celeritas/optical/detail/PreGenAction.cu -//---------------------------------------------------------------------------// -#include "PreGenAction.hh" - -#include -#include -#include - -#include "corecel/Assert.hh" -#include "corecel/sys/ScopedProfiling.hh" -#include "corecel/sys/Thrust.device.hh" -#include "celeritas/global/ActionLauncher.device.hh" -#include "celeritas/global/CoreParams.hh" -#include "celeritas/global/CoreState.hh" -#include "celeritas/global/TrackExecutor.hh" -#include "celeritas/optical/CerenkovParams.hh" -#include "celeritas/optical/OpticalPropertyParams.hh" -#include "celeritas/optical/ScintillationParams.hh" - -#include "OpticalGenStorage.hh" -#include "PreGenExecutor.hh" - -namespace celeritas -{ -namespace detail -{ -//---------------------------------------------------------------------------// -/*! - * Launch a kernel to generate optical distribution data post-step. - */ -void PreGenAction::pre_generate(CoreParams const& core_params, - CoreStateDevice& core_state) const -{ - TrackExecutor execute{ - core_params.ptr(), - core_state.ptr(), - detail::PreGenExecutor{properties_->device_ref(), - cerenkov_->device_ref(), - scintillation_->device_ref(), - storage_->obj.state( - core_state.stream_id(), core_state.size()), - storage_->size[core_state.stream_id().get()]}}; - static ActionLauncher const launch_kernel(*this); - launch_kernel(core_state, execute); -} - -//---------------------------------------------------------------------------// -/*! - * Remove all invalid distributions from the buffer. - */ -size_type -PreGenAction::remove_if_invalid(ItemsRef const& buffer, - size_type offset, - size_type size, - StreamId stream) const -{ - ScopedProfiling profile_this{"remove-if-invalid"}; - auto start = thrust::device_pointer_cast(buffer.data().get()); - auto stop = thrust::remove_if(thrust_execute_on(stream), - start + offset, - start + offset + size, - IsInvalid{}); - CELER_DEVICE_CHECK_ERROR(); - return stop - start; -} - -//---------------------------------------------------------------------------// -} // namespace detail -} // namespace celeritas diff --git a/src/celeritas/optical/detail/PreGenGatherAction.hh b/src/celeritas/optical/detail/PreGenGatherAction.hh index 33ea8bbb9f..c15b2e6efb 100644 --- a/src/celeritas/optical/detail/PreGenGatherAction.hh +++ b/src/celeritas/optical/detail/PreGenGatherAction.hh @@ -47,7 +47,7 @@ class PreGenGatherAction final : public ExplicitCoreActionInterface //! Short name for the action std::string_view label() const final { - return "optical-pre-generator-pre"; + return "optical-pre-generator-gather"; } // Name of the action (for user output) diff --git a/src/celeritas/optical/detail/ScintPreGenAction.cc b/src/celeritas/optical/detail/ScintPreGenAction.cc new file mode 100644 index 0000000000..0631209000 --- /dev/null +++ b/src/celeritas/optical/detail/ScintPreGenAction.cc @@ -0,0 +1,128 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/ScintPreGenAction.cc +//---------------------------------------------------------------------------// +#include "ScintPreGenAction.hh" + +#include + +#include "corecel/Assert.hh" +#include "celeritas/global/ActionLauncher.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/CoreTrackData.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/optical/ScintillationParams.hh" + +#include "OpticalGenAlgorithms.hh" +#include "OpticalGenStorage.hh" +#include "ScintPreGenExecutor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with action ID, optical properties, and storage. + */ +ScintPreGenAction::ScintPreGenAction(ActionId id, + SPConstScintillation scintillation, + SPGenStorage storage) + : id_(id) + , scintillation_(std::move(scintillation)) + , storage_(std::move(storage)) +{ + CELER_EXPECT(id_); + CELER_EXPECT(scintillation_); + CELER_EXPECT(storage_); +} + +//---------------------------------------------------------------------------// +/*! + * Descriptive name of the action. + */ +std::string_view ScintPreGenAction::description() const +{ + return "generate scintillation optical distribution data"; +} + +//---------------------------------------------------------------------------// +/*! + * Execute the action with host data. + */ +void ScintPreGenAction::execute(CoreParams const& params, + CoreStateHost& state) const +{ + this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Execute the action with device data. + */ +void ScintPreGenAction::execute(CoreParams const& params, + CoreStateDevice& state) const +{ + this->execute_impl(params, state); +} + +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data post-step. + */ +template +void ScintPreGenAction::execute_impl(CoreParams const& core_params, + CoreState& core_state) const +{ + size_type state_size = core_state.size(); + StreamId stream = core_state.stream_id(); + auto& buffer_size = storage_->size[stream.get()]; + auto const& state = storage_->obj.state(stream, state_size); + + CELER_VALIDATE( + buffer_size.scintillation + state_size <= state.scintillation.size(), + << "insufficient capacity (" << state.scintillation.size() + << ") for buffered scintillation distribution data (total " + "capacity requirement of " + << buffer_size.scintillation + state_size << ")"); + + // Generate the optical distribution data + this->pre_generate(core_params, core_state); + + // Compact the buffer + buffer_size.scintillation = remove_if_invalid( + state.scintillation, buffer_size.scintillation, state_size, stream); +} + +//---------------------------------------------------------------------------// +/*! + * Launch a (host) kernel to generate optical distribution data post-step. + */ +void ScintPreGenAction::pre_generate(CoreParams const& core_params, + CoreStateHost& core_state) const +{ + TrackExecutor execute{core_params.ptr(), + core_state.ptr(), + detail::ScintPreGenExecutor{ + scintillation_->host_ref(), + storage_->obj.state( + core_state.stream_id(), core_state.size()), + storage_->size[core_state.stream_id().get()]}}; + launch_action(*this, core_params, core_state, execute); +} + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +void ScintPreGenAction::pre_generate(CoreParams const&, CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/ScintPreGenAction.cu b/src/celeritas/optical/detail/ScintPreGenAction.cu new file mode 100644 index 0000000000..e8cd79590e --- /dev/null +++ b/src/celeritas/optical/detail/ScintPreGenAction.cu @@ -0,0 +1,46 @@ +//---------------------------------*-CUDA-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/ScintPreGenAction.cu +//---------------------------------------------------------------------------// +#include "ScintPreGenAction.hh" + +#include "corecel/Assert.hh" +#include "corecel/sys/ScopedProfiling.hh" +#include "celeritas/global/ActionLauncher.device.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/optical/ScintillationParams.hh" + +#include "OpticalGenAlgorithms.hh" +#include "OpticalGenStorage.hh" +#include "ScintPreGenExecutor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Launch a kernel to generate optical distribution data post-step. + */ +void ScintPreGenAction::pre_generate(CoreParams const& core_params, + CoreStateDevice& core_state) const +{ + TrackExecutor execute{core_params.ptr(), + core_state.ptr(), + detail::ScintPreGenExecutor{ + scintillation_->device_ref(), + storage_->obj.state( + core_state.stream_id(), core_state.size()), + storage_->size[core_state.stream_id().get()]}}; + static ActionLauncher const launch_kernel(*this); + launch_kernel(core_state, execute); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/ScintPreGenAction.hh b/src/celeritas/optical/detail/ScintPreGenAction.hh new file mode 100644 index 0000000000..e03a13893b --- /dev/null +++ b/src/celeritas/optical/detail/ScintPreGenAction.hh @@ -0,0 +1,82 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/ScintPreGenAction.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Macros.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/global/ActionInterface.hh" +#include "celeritas/optical/OpticalDistributionData.hh" + +namespace celeritas +{ +class ScintillationParams; + +namespace detail +{ +struct OpticalGenStorage; +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +class ScintPreGenAction final : public ExplicitCoreActionInterface +{ + public: + //!@{ + //! \name Type aliases + using SPConstScintillation = std::shared_ptr; + using SPGenStorage = std::shared_ptr; + //!@} + + public: + // Construct with action ID, optical properties, and storage + ScintPreGenAction(ActionId id, + SPConstScintillation scintillation, + SPGenStorage storage); + + // Launch kernel with host data + void execute(CoreParams const&, CoreStateHost&) const final; + + // Launch kernel with device data + void execute(CoreParams const&, CoreStateDevice&) const final; + + //! ID of the model + ActionId action_id() const final { return id_; } + + //! Short name for the action + std::string_view label() const final + { + return "scintillation-pre-generator"; + } + + // Name of the action (for user output) + std::string_view description() const final; + + //! Dependency ordering of the action + ActionOrder order() const final { return ActionOrder::post_post; } + + private: + //// DATA //// + + ActionId id_; + SPConstScintillation scintillation_; + SPGenStorage storage_; + + //// HELPER FUNCTIONS //// + + template + void execute_impl(CoreParams const&, CoreState&) const; + + void pre_generate(CoreParams const&, CoreStateHost&) const; + void pre_generate(CoreParams const&, CoreStateDevice&) const; +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/ScintPreGenExecutor.hh b/src/celeritas/optical/detail/ScintPreGenExecutor.hh new file mode 100644 index 0000000000..5b36efe0ef --- /dev/null +++ b/src/celeritas/optical/detail/ScintPreGenExecutor.hh @@ -0,0 +1,80 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/ScintPreGenExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "celeritas/global/CoreTrackView.hh" +#include "celeritas/optical/OpticalGenData.hh" +#include "celeritas/optical/ScintillationPreGenerator.hh" + +#include "Utils.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +// LAUNCHER +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +struct ScintPreGenExecutor +{ + inline CELER_FUNCTION void + operator()(celeritas::CoreTrackView const& track); + + NativeCRef const scintillation; + NativeRef const state; + OpticalBufferSize size; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Generate optical distribution data. + */ +CELER_FUNCTION void ScintPreGenExecutor::operator()(CoreTrackView const& track) +{ + CELER_EXPECT(state); + + using DistId = ItemId; + + auto tsid = track.track_slot_id(); + CELER_ASSERT(size.scintillation + tsid.get() < state.scintillation.size()); + auto& scintillation_dist + = state.scintillation[DistId(size.scintillation + tsid.get())]; + + // Clear distribution data + scintillation_dist = {}; + + auto sim = track.make_sim_view(); + auto optmat_id = get_optical_material(track); + if (!optmat_id || sim.step_length() == 0) + { + // Inactive tracks, materials with no optical properties, or particles + // that started the step with zero energy (e.g. a stopped positron) + return; + } + + Real3 const& pos = track.make_geo_view().pos(); + auto edep = track.make_physics_step_view().energy_deposition(); + auto particle = track.make_particle_view(); + auto rng = track.make_rng_engine(); + + // Get the distribution data used to generate scintillation optical photons + ScintillationPreGenerator generate( + particle, sim, pos, optmat_id, edep, scintillation, state.step[tsid]); + scintillation_dist = generate(rng); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/optical/detail/Utils.hh b/src/celeritas/optical/detail/Utils.hh new file mode 100644 index 0000000000..06210cdcac --- /dev/null +++ b/src/celeritas/optical/detail/Utils.hh @@ -0,0 +1,29 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/optical/detail/Utils.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/global/CoreTrackView.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +// Get the optical material ID, or an invalid ID if the track is inactive +inline CELER_FUNCTION OpticalMaterialId +get_optical_material(CoreTrackView const& track) +{ + if (track.make_sim_view().status() == TrackStatus::inactive) + return {}; + + return track.make_material_view().make_material_view().optical_material_id(); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/test/celeritas/optical/OpticalCollector.test.cc b/test/celeritas/optical/OpticalCollector.test.cc index 78cc9fd9aa..7ed9cc8221 100644 --- a/test/celeritas/optical/OpticalCollector.test.cc +++ b/test/celeritas/optical/OpticalCollector.test.cc @@ -58,10 +58,12 @@ class LArSpherePreGenTest : public LArSphereBase }; public: - void SetUp() override; + void SetUp() override {} SPConstAction build_along_step() override; + void build_optical_collector(); + VecPrimary make_primaries(size_type count); template @@ -74,27 +76,10 @@ class LArSpherePreGenTest : public LArSphereBase std::shared_ptr collector_; StreamId stream_{0}; + bool use_scintillation_{true}; + bool use_cerenkov_{true}; }; -//---------------------------------------------------------------------------// -/*! - * Construct optical collector at setup time. - */ -void LArSpherePreGenTest::SetUp() -{ - auto& action_reg = *this->action_reg(); - - OpticalCollector::Input inp; - inp.properties = this->properties(); - inp.cerenkov = this->cerenkov(); - inp.scintillation = this->scintillation(); - inp.action_registry = &action_reg; - inp.buffer_capacity = 256; - inp.num_streams = 1; - - collector_ = std::make_shared(inp); -} - //---------------------------------------------------------------------------// //! Print the expected result void LArSpherePreGenTest::RunResult::print_expected() const @@ -155,6 +140,31 @@ auto LArSpherePreGenTest::build_along_step() -> SPConstAction return result; } +//---------------------------------------------------------------------------// +/*! + * Construct optical collector. + */ +void LArSpherePreGenTest::build_optical_collector() +{ + auto& action_reg = *this->action_reg(); + + OpticalCollector::Input inp; + if (use_cerenkov_) + { + inp.properties = this->properties(); + inp.cerenkov = this->cerenkov(); + } + if (use_scintillation_) + { + inp.scintillation = this->scintillation(); + } + inp.action_registry = &action_reg; + inp.buffer_capacity = 256; + inp.num_streams = 1; + + collector_ = std::make_shared(inp); +} + //---------------------------------------------------------------------------// /*! * Generate a vector of primary particles. @@ -267,6 +277,7 @@ template LArSpherePreGenTest::RunResult TEST_F(LArSpherePreGenTest, host) { + this->build_optical_collector(); auto result = this->run(4, 64); static real_type const expected_cerenkov_charge[] = {-1, 1}; @@ -320,6 +331,7 @@ TEST_F(LArSpherePreGenTest, host) TEST_F(LArSpherePreGenTest, TEST_IF_CELER_DEVICE(device)) { + this->build_optical_collector(); auto result = this->run(8, 32); static real_type const expected_cerenkov_charge[] = {-1, 1}; @@ -386,6 +398,50 @@ TEST_F(LArSpherePreGenTest, TEST_IF_CELER_DEVICE(device)) } } +TEST_F(LArSpherePreGenTest, only_cerenkov) +{ + use_scintillation_ = false; + this->build_optical_collector(); + + auto result = this->run(4, 16); + + EXPECT_EQ(0, result.scintillation.total_num_photons); + EXPECT_EQ(0, result.scintillation.num_photons.size()); + + if (CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) + { + EXPECT_EQ(19601, result.cerenkov.total_num_photons); + EXPECT_EQ(37, result.cerenkov.num_photons.size()); + } + else + { + EXPECT_EQ(20790, result.cerenkov.total_num_photons); + EXPECT_EQ(43, result.cerenkov.num_photons.size()); + } +} + +TEST_F(LArSpherePreGenTest, only_scintillation) +{ + use_cerenkov_ = false; + this->build_optical_collector(); + + auto result = this->run(4, 16); + + EXPECT_EQ(0, result.cerenkov.total_num_photons); + EXPECT_EQ(0, result.cerenkov.num_photons.size()); + + if (CELERITAS_REAL_TYPE == CELERITAS_REAL_TYPE_DOUBLE) + { + EXPECT_EQ(1629295, result.scintillation.total_num_photons); + EXPECT_EQ(53, result.scintillation.num_photons.size()); + } + else + { + EXPECT_EQ(1656334, result.scintillation.total_num_photons); + EXPECT_EQ(52, result.scintillation.num_photons.size()); + } +} + //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas From 6684723ac85326d2453c05f8b2a9ba91fd5c0d00 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 3 May 2024 16:27:35 -0400 Subject: [PATCH 36/59] Fix minor dependency issues (#1219) * Fix include paths * Fix missing geant4 library dependency --- src/celeritas/global/KernelContextException.hh | 4 +++- src/geocel/CMakeLists.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/celeritas/global/KernelContextException.hh b/src/celeritas/global/KernelContextException.hh index df3c4806c3..fbff47917a 100644 --- a/src/celeritas/global/KernelContextException.hh +++ b/src/celeritas/global/KernelContextException.hh @@ -10,7 +10,6 @@ #include #include "corecel/Assert.hh" -#include "corecel/io/JsonPimpl.hh" #include "celeritas/Quantities.hh" #include "celeritas/Types.hh" @@ -18,7 +17,10 @@ namespace celeritas { +//---------------------------------------------------------------------------// class CoreTrackView; +struct JsonPimpl; + //---------------------------------------------------------------------------// /*! * Provide contextual information about failed errors on CPU. diff --git a/src/geocel/CMakeLists.txt b/src/geocel/CMakeLists.txt index c31b8286cf..9ce5941416 100644 --- a/src/geocel/CMakeLists.txt +++ b/src/geocel/CMakeLists.txt @@ -54,7 +54,7 @@ if(CELERITAS_USE_Geant4) g4/RaytraceImager.cc g4/detail/GeantGeoNavCollection.cc ) - celeritas_get_g4libs(_cg4_libs global geometry persistency intercoms) + celeritas_get_g4libs(_cg4_libs global geometry materials persistency intercoms) list(APPEND _cg4_libs Celeritas::corecel XercesC::XercesC) celeritas_add_object_library(geocel_geant4 ${_cg4_sources}) From a8c69d2bd85817e29df144fd61523fbc6e05c535 Mon Sep 17 00:00:00 2001 From: Guilherme Lima Date: Mon, 6 May 2024 23:52:01 -0500 Subject: [PATCH 37/59] Add ORANGE converter for G4Trd (#1218) * Add ORANGE converter for G4Trd * Add a TRD-like GenTrap constructor for reusability * Add helper functions to calculate corners for GenTrap construction The helper functions have arguments that correspond to the main G4Trap and G4Trd constructors, but arranges those parameters into z-face rectangles or trapezes. Testing cases were added for the new helper functions. * Apply reviewer suggestions * Fix comparison tolerance for planarity * Rearrange constructors for consistency * Remove failing test to be deleted soon --- src/orange/g4org/SolidConverter.cc | 47 ++++---- src/orange/orangeinp/ConvexRegion.cc | 76 ++++++++++++ src/orange/orangeinp/ConvexRegion.hh | 28 ++++- src/orange/orangeinp/detail/PolygonUtils.hh | 4 +- test/orange/g4org/SolidConverter.test.cc | 69 +++++++---- test/orange/orangeinp/ConvexRegion.test.cc | 110 ++++++++++++++++-- .../orangeinp/detail/PolygonUtils.test.cc | 29 ----- 7 files changed, 277 insertions(+), 86 deletions(-) diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index db6ed1b254..145c5ccc86 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -609,46 +609,40 @@ auto SolidConverter::trap(arg_type solid_base) -> result_type { auto const& solid = dynamic_cast(solid_base); - real_type sin_phi{}, cos_phi{}, tan_theta{}; + real_type tan_theta{}; + Turn phi{}; #if G4VERSION_NUMBER < 1100 // Geant4 10.7 and earlier - axis = (sinth.cosphi, sinth.sinphi, costh) // Note: both sinth,costh >= 0 since theta is in [0, pi/2] for a G4Trap auto axis = solid.GetSymAxis(); - auto sin_theta = std::sqrt(real_type(1.0) - axis.z() * axis.z()); + auto sin_theta = std::max(0.0, std::sqrt(1.0 - ipow<2>(axis.z()))); tan_theta = SoftZero{1.e-8}(axis.z()) ? sin_theta / axis.z() : numeric_limits::infinity(); - cos_phi = sin_theta > 0 ? axis.x() / sin_theta : 1.0; - sin_phi = sin_theta > 0 ? axis.y() / sin_theta : 1.0; + real_type cos_phi = sin_theta > 0 ? axis.x() / sin_theta : 1.0; + real_type sin_phi = sin_theta > 0 ? axis.y() / sin_theta : 1.0; + phi = native_value_to(std::atan2(sin_phi, cos_phi)); #else // Geant4 11 and later - sincos(solid.GetPhi(), &sin_phi, &cos_phi); tan_theta = std::tan(solid.GetTheta()); + phi = native_value_to(solid.GetPhi()); #endif auto hz = scale_(solid.GetZHalfLength()); auto hy1 = scale_(solid.GetYHalfLength1()); - auto hy2 = scale_(solid.GetYHalfLength2()); auto hx1 = scale_(solid.GetXHalfLength1()); auto hx2 = scale_(solid.GetXHalfLength2()); + auto hy2 = scale_(solid.GetYHalfLength2()); auto hx3 = scale_(solid.GetXHalfLength3()); auto hx4 = scale_(solid.GetXHalfLength4()); - auto dxdz_hz = tan_theta * cos_phi * hz; - auto dydz_hz = tan_theta * sin_phi * hz; - auto dxdy_hy1 = solid.GetTanAlpha1() * hy1; - auto dxdy_hy2 = solid.GetTanAlpha2() * hy2; - - std::vector lower(4), upper(4); - lower[0] = GenTrap::Real2{-dxdz_hz - dxdy_hy1 - hx1, -dydz_hz - hy1}; - lower[1] = GenTrap::Real2{-dxdz_hz - dxdy_hy1 + hx1, -dydz_hz - hy1}; - lower[2] = GenTrap::Real2{-dxdz_hz + dxdy_hy1 + hx2, -dydz_hz + hy1}; - lower[3] = GenTrap::Real2{-dxdz_hz + dxdy_hy1 - hx2, -dydz_hz + hy1}; - upper[0] = GenTrap::Real2{+dxdz_hz - dxdy_hy2 - hx3, +dydz_hz - hy2}; - upper[1] = GenTrap::Real2{+dxdz_hz - dxdy_hy2 + hx3, +dydz_hz - hy2}; - upper[2] = GenTrap::Real2{+dxdz_hz + dxdy_hy2 + hx4, +dydz_hz + hy2}; - upper[3] = GenTrap::Real2{+dxdz_hz + dxdy_hy2 - hx4, +dydz_hz + hy2}; - return make_shape(solid, hz, lower, upper); + return make_shape( + solid, + GenTrap::from_trap(hz, + tan_theta, + phi, + {hy1, hx1, hx2, solid.GetTanAlpha1()}, + {hy2, hx3, hx4, solid.GetTanAlpha2()})); } //---------------------------------------------------------------------------// @@ -656,8 +650,15 @@ auto SolidConverter::trap(arg_type solid_base) -> result_type auto SolidConverter::trd(arg_type solid_base) -> result_type { auto const& solid = dynamic_cast(solid_base); - CELER_DISCARD(solid); - CELER_NOT_IMPLEMENTED("trd"); + + auto hz = scale_(solid.GetZHalfLength()); + auto hy1 = scale_(solid.GetYHalfLength1()); + auto hy2 = scale_(solid.GetYHalfLength2()); + auto hx1 = scale_(solid.GetXHalfLength1()); + auto hx2 = scale_(solid.GetXHalfLength2()); + + return make_shape(solid, + GenTrap::from_trd(hz, {hx1, hy1}, {hx2, hy2})); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/ConvexRegion.cc index ddfeb7ab3f..75c62222fa 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/ConvexRegion.cc @@ -314,6 +314,82 @@ void Ellipsoid::output(JsonPimpl* j) const //---------------------------------------------------------------------------// // GENTRAP +//---------------------------------------------------------------------------// +/*! + * Construct a TRD from 5 half-lengths: one half-height, {hx1,hy1} for a in -z, + * and {hx2,hy2} in +z + */ +GenTrap GenTrap::from_trd(real_type halfz, Real2 const& lo, Real2 const& hi) +{ + CELER_VALIDATE(lo[0] > 0, << "nonpositive lower x half-edge: " << lo[0]); + CELER_VALIDATE(hi[0] > 0, << "nonpositive upper x half-edge: " << hi[0]); + CELER_VALIDATE(lo[1] > 0, << "nonpositive lower y half-edge: " << lo[1]); + CELER_VALIDATE(hi[1] > 0, << "nonpositive upper y half-edge: " << hi[1]); + CELER_VALIDATE(halfz > 0, << "nonpositive half-height: " << halfz); + + // Construct points counterclockwise from lower left + VecReal2 lower + = {{-lo[0], -lo[1]}, {lo[0], -lo[1]}, {lo[0], lo[1]}, {-lo[0], lo[1]}}; + VecReal2 upper + = {{-hi[0], -hi[1]}, {hi[0], -hi[1]}, {hi[0], hi[1]}, {-hi[0], hi[1]}}; + + return GenTrap{halfz, std::move(lower), std::move(upper)}; +} + +//---------------------------------------------------------------------------// +/*! + * Construct from half Z height and 1-4 vertices for top and bottom planes. + */ +GenTrap GenTrap::from_trap(real_type hz, + real_type tan_theta, + Turn const& phi, + TrapFace const& lo, + TrapFace const& hi) +{ + // TrapFace is validated in its constructor + CELER_VALIDATE(hz > 0, << "nonpositive half-height: " << hz); + + CELER_VALIDATE(tan_theta >= 0, << "negative tan(theta): " << tan_theta); + + CELER_VALIDATE(phi >= zero_quantity() && phi < Turn{1.}, + << "invalid angle " << phi.value() + << " [turns]: must be in the range [0, 1)"); + + for (TrapFace const* tf : {&lo, &hi}) + { + CELER_VALIDATE(tf->hx_lo_ > 0, + << "nonpositive lower x half-edge: " << tf->hx_lo_); + CELER_VALIDATE(tf->hx_hi_ > 0, + << "nonpositive upper x half-edge: " << tf->hx_hi_); + CELER_VALIDATE(tf->hy_ > 0, + << "nonpositive y half-distance: " << tf->hy_); + } + real_type cos_phi{}, sin_phi{}; + sincos(phi, &sin_phi, &cos_phi); + auto dxdz_hz = tan_theta * cos_phi * hz; + auto dydz_hz = tan_theta * sin_phi * hz; + + auto hy1 = lo.hy_; + auto hx1 = lo.hx_lo_; + auto hx2 = lo.hx_hi_; + auto hy2 = hi.hy_; + auto hx3 = hi.hx_lo_; + auto hx4 = hi.hx_hi_; + auto dxdy_hy1 = lo.tan_alpha_ * lo.hy_; + auto dxdy_hy2 = hi.tan_alpha_ * hi.hy_; + + VecReal2 lower = {{-dxdz_hz - dxdy_hy1 - hx1, -dydz_hz - hy1}, + {-dxdz_hz - dxdy_hy1 + hx1, -dydz_hz - hy1}, + {-dxdz_hz + dxdy_hy1 + hx2, -dydz_hz + hy1}, + {-dxdz_hz + dxdy_hy1 - hx2, -dydz_hz + hy1}}; + VecReal2 upper = {{+dxdz_hz - dxdy_hy2 - hx3, +dydz_hz - hy2}, + {+dxdz_hz - dxdy_hy2 + hx3, +dydz_hz - hy2}, + {+dxdz_hz + dxdy_hy2 + hx4, +dydz_hz + hy2}, + {+dxdz_hz + dxdy_hy2 - hx4, +dydz_hz + hy2}}; + + return GenTrap{hz, std::move(lower), std::move(upper)}; +} + //---------------------------------------------------------------------------// /*! * Construct from half Z height and 1-4 vertices for top and bottom planes. diff --git a/src/orange/orangeinp/ConvexRegion.hh b/src/orange/orangeinp/ConvexRegion.hh index 591f7b4b1a..db91969844 100644 --- a/src/orange/orangeinp/ConvexRegion.hh +++ b/src/orange/orangeinp/ConvexRegion.hh @@ -216,8 +216,34 @@ class GenTrap final : public ConvexRegionInterface using VecReal2 = std::vector; //!@} + //! Argument for building from regular trapezoidal top/bottom polygons + struct TrapFace + { + real_type hy_{}; //!< half the vertical distance between horizontal + //!< edges + real_type hx_lo_{}; //!< top horizontal edge half-length + real_type hx_hi_{}; //!< botton horizontal edge half-length + + // tan(alpha), where alpha is the clockwise angle between the + // _centers_ of horizontal edges, with respect to the vertical + // (alpha=0) + real_type tan_alpha_{}; + }; + public: - // Construct from half Z height and 1-4 vertices for top and bottom planes + // Helper function to construct a Trd shape from hz and two rectangles, + // one for each z-face + static GenTrap from_trd(real_type halfz, Real2 const& lo, Real2 const& hi); + + // Helper function to construct a general trap from its half-height and + // the two trapezoids defining its lower and upper faces + static GenTrap from_trap(real_type hz, + real_type tan_theta, + Turn const& phi, + TrapFace const& lo, + TrapFace const& hi); + + // Construct from half Z height and 4 vertices for top and bottom planes GenTrap(real_type halfz, VecReal2 const& lo, VecReal2 const& hi); // Build surfaces diff --git a/src/orange/orangeinp/detail/PolygonUtils.hh b/src/orange/orangeinp/detail/PolygonUtils.hh index 2ebb9f4aae..bed5aa9094 100644 --- a/src/orange/orangeinp/detail/PolygonUtils.hh +++ b/src/orange/orangeinp/detail/PolygonUtils.hh @@ -122,8 +122,8 @@ is_planar(Real3 const& a, Real3 const& b, Real3 const& c, Real3 const& d) auto norm = make_unit_vector(cross_product(b - a, c - a)); auto val = dot_product(norm, d - a); - // FIXME: SoftEqual and SoftZero should have rel = abs - return SoftZero{SoftEqual<>{}.rel()}(val); + return SoftZero{ + ::celeritas::detail::SoftEqualTraits::sqrt_prec()}(val); } //---------------------------------------------------------------------------// diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index 3838c0308a..82828f24fd 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -398,49 +398,76 @@ TEST_F(SolidConverterTest, subtractionsolid) {{0, 0, 0}, {0, 0, 10}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}}); } -TEST_F(SolidConverterTest, trap) +TEST_F(SolidConverterTest, trap_box) { { this->build_and_test( - G4Trap("boxTrap", 30, 0, 0, 20, 10, 10, 0, 20, 10, 10, 0), + G4Trap("trap_box", 30, 0, 0, 20, 10, 10, 0, 20, 10, 10, 0), R"json({"_type":"shape","interior":{"_type":"gentrap", "halfheight":3.0, "lower":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]], "upper":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]]}, - "label":"boxTrap"})json", + "label":"trap_box"})json", {{-1, -2, -3}, {1, 2, 3}, {1, 2, 4}}); } +} +TEST_F(SolidConverterTest, trap_trd) +{ { this->build_and_test( - G4Trap("trdGenTrap", 50, 100, 100, 200, 300), + G4Trap("trap_trd", 50, 100, 100, 200, 300), R"json({"_type":"shape","interior":{"_type":"gentrap", "halfheight":30.0, "lower":[[-5.0,-10.0],[5.0,-10.0],[5.0,10.0],[-5.0,10.0]], "upper":[[-10.0,-20.0],[10.0,-20.0],[10.0,20.0],[-10.0,20.0]]}, - "label":"trdGenTrap"})json", + "label":"trap_trd"})json", {{-10, -20, -40}, {-10, -20, -30 + 1.e-6}, {5, 10, 30}, {10, 10, 30}}); } +} - { - this->build_and_test( - G4Trap("trap", 40, 0.02, 0.05, 20, 10, 10, 0.01, 30, 15, 15, 0.01), - R"json({"_type":"shape","interior":{"_type":"gentrap", - "halfheight":4.0, - "lower":[[-1.0999113425658524,-2.0039988667381046], - [0.9000886574341476,-2.0039988667381046], - [0.9400899908208165,1.9960011332618952], - [-1.0599100091791835,1.9960011332618952]], - "upper":[[-1.4500903241674836,-2.9960011332618954], - [1.5499096758325164,-2.9960011332618954], - [1.6099116759125196,3.0039988667381046], - [-1.3900883240874804,3.0039988667381046]]}, - "label":"trap"})json", - {{-1, -2, -4 - 1.e-6}, {-1, -2, -3}, {0.5, 1, 3}, {1, 1, 3}}); - } +TEST_F(SolidConverterTest, trap) +{ + this->build_and_test( + G4Trap("trap", 40, 0.02, 0.05, 20, 10, 10, 0.01, 30, 15, 15, 0.01), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":4.0, + "lower":[[-1.0999113425658524,-2.0039988667381046], + [0.9000886574341476,-2.0039988667381046], + [0.9400899908208165,1.9960011332618952], + [-1.0599100091791835,1.9960011332618952]], + "upper":[[-1.4500903241674836,-2.9960011332618954], + [1.5499096758325164,-2.9960011332618954], + [1.6099116759125196,3.0039988667381046], + [-1.3900883240874804,3.0039988667381046]]}, + "label":"trap"})json", + {{-1, -2, -4 - 1.e-6}, {-1, -2, -3}, {0.5, 1, 3}, {1, 1, 3}}); +} + +TEST_F(SolidConverterTest, trd_box) +{ + this->build_and_test(G4Trd("trd_box", 10, 10, 20, 20, 30), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":3.0, + "lower":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]], + "upper":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]]}, + "label":"trd_box"})json", + {{-1, -2, -3}, {1, 2, 3}, {1, 2, 4}}); +} + +TEST_F(SolidConverterTest, trd) +{ + this->build_and_test( + G4Trd("trd", 50, 100, 100, 200, 300), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":30.0, + "lower":[[-5.0,-10.0],[5.0,-10.0],[5.0,10.0],[-5.0,10.0]], + "upper":[[-10.0,-20.0],[10.0,-20.0],[10.0,20.0],[-10.0,20.0]]}, + "label":"trd"})json", + {{-10, -20, -40}, {-10, -20, -30 + 1.e-6}, {5, 10, 30}, {10, 10, 30}}); } TEST_F(SolidConverterTest, tubs) diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index b888315c9f..11348aff43 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -425,6 +425,13 @@ TEST_F(GenTrapTest, construct) {{-2, -2}, {-2, 2}, {2, 2}, {2, -2}}), RuntimeError); // non-convex + // Validate TRD-like construction parameters - 5 half-lengths + EXPECT_THROW(GenTrap::from_trd(-3, {1, 1}, {2, 2}), RuntimeError); // dZ<0 + EXPECT_THROW(GenTrap::from_trd(3, {-1, 1}, {2, 2}), RuntimeError); // hx1<0 + EXPECT_THROW(GenTrap::from_trd(3, {1, -1}, {2, 2}), RuntimeError); // hy1<0 + EXPECT_THROW(GenTrap::from_trd(3, {1, 1}, {-2, 2}), RuntimeError); // hx2<0 + EXPECT_THROW(GenTrap::from_trd(3, {1, 1}, {2, -2}), RuntimeError); // hy2<0 + // General non-planar GenTrap with 'twisted' faces is not yet implemented EXPECT_THROW(GenTrap(3, {{-10, -10}, {-10, 10}, {10, 10}, {9, -11}}, @@ -435,26 +442,26 @@ TEST_F(GenTrapTest, construct) TEST_F(GenTrapTest, box_like) { auto result = this->test(GenTrap(3, - {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, - {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}})); + {{-1, -2}, {1, -2}, {1, 2}, {-1, 2}}, + {{-1, -2}, {1, -2}, {1, 2}, {-1, 2}})); static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; static char const* const expected_surfaces[] = {"Plane: z=-3", "Plane: z=3", - "Plane: y=-1", + "Plane: y=-2", "Plane: x=1", - "Plane: y=1", + "Plane: y=2", "Plane: x=-1"}; EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); - EXPECT_VEC_SOFT_EQ((Real3{-1, -1, -3}), result.interior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{1, 1, 3}), result.interior.upper()); - EXPECT_VEC_SOFT_EQ((Real3{-1, -1, -3}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{1, 1, 3}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-1, -2, -3}), result.interior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{1, 2, 3}), result.interior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-1, -2, -3}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{1, 2, 3}), result.exterior.upper()); } -TEST_F(GenTrapTest, trd) +TEST_F(GenTrapTest, trd1) { auto result = this->test(GenTrap(3, {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, @@ -476,6 +483,26 @@ TEST_F(GenTrapTest, trd) EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); } +TEST_F(GenTrapTest, trd2) +{ + auto result = this->test(GenTrap::from_trd(3, {1, 1}, {2, 2})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-3", + "Plane: z=3", + "Plane: n={0,0.98639,0.1644}, d=-1.4796", + "Plane: n={0.98639,0,-0.1644}, d=1.4796", + "Plane: n={0,0.98639,-0.1644}, d=1.4796", + "Plane: n={0.98639,0,0.1644}, d=-1.4796"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -3}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); +} + TEST_F(GenTrapTest, ppiped) { auto result = this->test(GenTrap(4, @@ -519,7 +546,7 @@ TEST_F(GenTrapTest, triang_prism) EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); } -TEST_F(GenTrapTest, trapezoid) +TEST_F(GenTrapTest, trap_corners) { auto result = this->test(GenTrap(40, @@ -591,6 +618,69 @@ TEST_F(GenTrapTest, trapezoid_ccw) EXPECT_VEC_SOFT_EQ((Real3{inf, 30, 40}), result.exterior.upper()); } +TEST_F(GenTrapTest, trap_theta) +{ + auto result = this->test( + GenTrap::from_trap(40, 1, Turn{0}, {20, 10, 10, 0}, {20, 10, 10, 0})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-40", + "Plane: z=40", + "Plane: y=-20", + "Plane: n={0.70711,0,-0.70711}, d=7.0711", + "Plane: y=20", + "Plane: n={0.70711,0,-0.70711}, d=-7.0711"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -20, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, 20, 40}), result.exterior.upper()); +} + +TEST_F(GenTrapTest, trap_thetaphi) +{ + auto result = this->test(GenTrap::from_trap( + 40, 1, Turn{0.25}, {20, 10, 10, 0}, {20, 10, 10, 0})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-40", + "Plane: z=40", + "Plane: n={0,0.70711,-0.70711}, d=-14.142", + "Plane: x=10", + "Plane: n={0,0.70711,-0.70711}, d=14.142", + "Plane: x=-10"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-10, -inf, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{10, inf, 40}), result.exterior.upper()); +} + +TEST_F(GenTrapTest, trap_full) +{ + auto result = this->test(GenTrap::from_trap( + 40, 1, Turn{0.125}, {20, 10, 10, 0.1}, {20, 10, 10, 0.1})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-40", + "Plane: z=40", + "Plane: n={0,0.8165,-0.57735}, d=-16.33", + "Plane: n={0.84066,-0.084066,-0.53499}, d=8.4066", + "Plane: n={0,0.8165,-0.57735}, d=16.33", + "Plane: n={0.84066,-0.084066,-0.53499}, d=-8.4066"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 40}), result.exterior.upper()); +} + // TODO: this should be valid TEST_F(GenTrapTest, DISABLED_pentahedron) { diff --git a/test/orange/orangeinp/detail/PolygonUtils.test.cc b/test/orange/orangeinp/detail/PolygonUtils.test.cc index e9c535512a..6f6a6f37ca 100644 --- a/test/orange/orangeinp/detail/PolygonUtils.test.cc +++ b/test/orange/orangeinp/detail/PolygonUtils.test.cc @@ -110,35 +110,6 @@ TEST(PolygonUtilsTest, planar) EXPECT_FALSE(is_planar(a, b, c, Real3{0, 0, 0})); } -TEST(PolygonUtilsTest, planar_tolerance) -{ - real_type const eps_lo = real_type{0.1} * SoftEqual<>{}.rel(); - real_type const eps_hi = 2 * SoftEqual<>{}.rel(); - Real3 a{-2, 2, -2}, b{2, 2, -2}, c{2, 2, 2}, d{-2, 2, 2}; - Real3 dy{0, 1, 0}; - - // effect on reference corner - Real3 aa{a}; - axpy(eps_lo, dy, &aa); - EXPECT_TRUE(is_planar(aa, b, c, d)); - axpy(eps_hi, dy, &aa); - EXPECT_FALSE(is_planar(aa, b, c, d)); - - // effect on non-ref corner - Real3 bb{b}; - axpy(eps_lo, dy, &bb); - EXPECT_TRUE(is_planar(a, bb, c, d)); - axpy(eps_hi, dy, &bb); - EXPECT_FALSE(is_planar(a, bb, c, d)); - - // effect on test corner - Real3 dd{d}; - axpy(eps_lo, dy, &dd); - EXPECT_TRUE(is_planar(a, b, c, dd)); - axpy(eps_hi, dy, &dd); - EXPECT_FALSE(is_planar(a, b, c, dd)); -} - //---------------------------------------------------------------------------// } // namespace test } // namespace detail From 12c9dbe9eb0935be57ea4f7e8c1ba7bb8151ad17 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 7 May 2024 14:06:49 -0400 Subject: [PATCH 38/59] Add configure-time `CELERITAS_OPENMP` switch to change threading (#1222) * Comment cmake config * Add cmake for openmp * Switch openmp pragmas based on configure flag * Fix ccache key for ultralite builds * Update warning message in accel * Overwrite tags * Address review feedback * Escape macros for launcher --- .github/workflows/build-ultralite.yml | 4 +- CMakeLists.txt | 54 ++++++++++++++++---- app/celer-sim/Runner.cc | 2 +- app/celer-sim/celer-sim.cc | 2 +- scripts/ci/run-ci.sh | 2 +- src/CMakeLists.txt | 7 +-- src/accel/LocalTransporter.cc | 5 +- src/celeritas/global/ActionLauncher.hh | 3 +- src/celeritas/track/detail/TrackSortUtils.cc | 4 +- src/celeritas/user/detail/SimpleCaloImpl.cc | 5 +- src/celeritas_config.h.in | 2 + src/corecel/math/Atomics.hh | 13 +++-- src/corecel/sys/MultiExceptionHandler.cc | 2 +- src/geocel/rasterize/RaytraceImager.t.hh | 2 +- 14 files changed, 80 insertions(+), 27 deletions(-) diff --git a/.github/workflows/build-ultralite.yml b/.github/workflows/build-ultralite.yml index 5746120ace..e662dbc3f3 100644 --- a/.github/workflows/build-ultralite.yml +++ b/.github/workflows/build-ultralite.yml @@ -28,7 +28,7 @@ jobs: uses: actions/cache@v4 with: path: ${{env.CCACHE_DIR}} - key: ccache-ultralite-${{github.run_id}} + key: ccache-ultralite-ubuntu-${{github.run_id}} restore-keys: | ccache-ultralite - name: Zero ccache stats @@ -80,7 +80,7 @@ jobs: uses: actions/cache@v4 with: path: ${{env.CCACHE_DIR}} - key: ccache-ultralite-${{github.run_id}} + key: ccache-ultralite-windows-${{github.run_id}} restore-keys: | ccache-ultralite - name: Zero ccache stats diff --git a/CMakeLists.txt b/CMakeLists.txt index d3ab928b89..ab7084451c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,16 +80,31 @@ cmake_dependent_option(CELERITAS_TEST_VERBOSE # CELERITAS CORE IMPLEMENTATION OPTIONS #----------------------------------------------------------------------------# -# CELERITAS_CORE_RNG: random number generator selection +if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP) + set(CELERITAS_MAX_BLOCK_SIZE 256 + CACHE STRING "Threads-per-block launch bound for Celeritas action kernels" + ) +else() + set(CELERITAS_MAX_BLOCK_SIZE 0) +endif() + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CELERITAS_CORE_RNG +# Random number generator selection +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ celeritas_setup_option(CELERITAS_CORE_RNG xorwow) celeritas_setup_option(CELERITAS_CORE_RNG cuRAND CELERITAS_USE_CUDA) celeritas_setup_option(CELERITAS_CORE_RNG hipRAND CELERITAS_USE_HIP) -# TODO: allow wrapper to standard library RNG when not building for device? +# TODO: add wrapper to standard library RNG when not building for device? +# TODO: add ranluxpp? # TODO: maybe even add wrapper to Geant4 RNG?? celeritas_define_options(CELERITAS_CORE_RNG "Celeritas runtime random number generator") -# CELERITAS_CORE_GEO: runtime geometry selection +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CELERITAS_CORE_GEO +# Runtime geometry selection +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if(CELERITAS_USE_VecGeom AND NOT CELERITAS_USE_HIP) set(_allow_vecgeom TRUE) else() @@ -98,7 +113,7 @@ else() message(SEND_ERROR "VecGeom core geometry is incompatible with HIP") endif() endif() -if(CELERITAS_USE_Geant4 AND NOT (CELERITAS_USE_HIP OR CELERITAS_USE_CUDA OR CELERITAS_USE_OpenMP)) +if(CELERITAS_USE_Geant4 AND NOT (CELERITAS_USE_HIP OR CELERITAS_USE_CUDA OR CELERITAS_OPENMP)) set(_allow_g4 TRUE) else() if(CELERITAS_CORE_GEO STREQUAL "Geant4") @@ -111,14 +126,26 @@ celeritas_setup_option(CELERITAS_CORE_GEO ORANGE) celeritas_setup_option(CELERITAS_CORE_GEO Geant4 _allow_g4) celeritas_define_options(CELERITAS_CORE_GEO "Celeritas runtime geometry") -if(CELERITAS_USE_CUDA OR CELERITAS_USE_HIP) - set(CELERITAS_MAX_BLOCK_SIZE 256 - CACHE STRING "Threads-per-block launch bound for Celeritas action kernels" - ) +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CELERITAS_OPENMP +# Thread parallelism +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if(CELERITAS_USE_OpenMP) + set(_disable_openmp FALSE) else() - set(CELERITAS_MAX_BLOCK_SIZE 0) + set(_disable_openmp TRUE) endif() +celeritas_setup_option(CELERITAS_OPENMP disabled _disable_openmp) +celeritas_setup_option(CELERITAS_OPENMP event CELERITAS_USE_OpenMP) +celeritas_setup_option(CELERITAS_OPENMP track CELERITAS_USE_OpenMP) +celeritas_define_options(CELERITAS_OPENMP "Celeritas OpenMP parallelism") + +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CELERITAS_UNITS +# Unit system for celeritas runtime +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if(CELERITAS_CORE_GEO STREQUAL Geant4) set(_allow_single_prec FALSE) else() @@ -131,6 +158,10 @@ celeritas_setup_option(CELERITAS_UNITS CLHEP) celeritas_define_options(CELERITAS_UNITS "Native unit system for Celeritas") +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# CELERITAS_REAL_TYPE +# Precision for real numbers +#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ celeritas_setup_option(CELERITAS_REAL_TYPE double) celeritas_setup_option(CELERITAS_REAL_TYPE float) celeritas_define_options(CELERITAS_REAL_TYPE @@ -269,6 +300,11 @@ endif() if(CELERITAS_USE_Geant4 AND NOT Geant4_FOUND) find_package(Geant4 REQUIRED) + if(CELERITAS_OPENMP STREQUAL "track" AND Geant4_multithreaded_FOUND) + message(WARNING "Track-level OpenMP will conflict with MT runs of geant4:" + "consider setting CELERITAS_OPENMP to \"event\" or disabling OpenMP" + ) + endif() endif() if(CELERITAS_USE_HepMC3) diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 2394278584..9e86e9bd85 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -89,7 +89,7 @@ namespace size_type calc_num_streams(RunnerInput const& inp, size_type num_events) { size_type num_threads = 1; -#if CELERITAS_USE_OPENMP +#if CELERITAS_OPENMP == CELERITAS_OPENMP_EVENT if (!inp.merge_events) { # pragma omp parallel diff --git a/app/celer-sim/celer-sim.cc b/app/celer-sim/celer-sim.cc index fc71a4effc..15cd0626b4 100644 --- a/app/celer-sim/celer-sim.cc +++ b/app/celer-sim/celer-sim.cc @@ -125,7 +125,7 @@ void run(std::istream* is, std::shared_ptr output) CELER_LOG(status) << "Transporting " << run_stream.num_events() << " on " << num_streams << " threads"; MultiExceptionHandler capture_exception; -#ifdef _OPENMP +#if CELERITAS_OPENMP == CELERITAS_OPENMP_EVENT # pragma omp parallel for #endif for (size_type event = 0; event < run_stream.num_events(); ++event) diff --git a/scripts/ci/run-ci.sh b/scripts/ci/run-ci.sh index 1c0c5bd142..4c7f67cca5 100755 --- a/scripts/ci/run-ci.sh +++ b/scripts/ci/run-ci.sh @@ -20,7 +20,7 @@ if [ -f "${_ENV_SCRIPT}" ]; then fi # Fetch tags for version provenance -git fetch --tags +git fetch -f --tags # Clean older builds from jenkins *BEFORE* setting up presets git clean -fxd diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3144fdd36..c883e1672a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,10 +25,11 @@ set(CELERITAS_USE_VECGEOM ${CELERITAS_USE_VecGeom}) # Start counter from 1 because undefined config have the implicit value of 0 in # the C preprocessor, so any unavailable options (e.g. CELERITAS_USE_CURAND # when HIP is in use) will implicitly be zero. -celeritas_generate_option_config(CELERITAS_CORE_RNG) celeritas_generate_option_config(CELERITAS_CORE_GEO) -celeritas_generate_option_config(CELERITAS_UNITS) +celeritas_generate_option_config(CELERITAS_CORE_RNG) +celeritas_generate_option_config(CELERITAS_OPENMP) celeritas_generate_option_config(CELERITAS_REAL_TYPE) +celeritas_generate_option_config(CELERITAS_UNITS) celeritas_configure_file("celeritas_config.h.in" "celeritas_config.h" @ONLY) #----------------------------------------------------------------------------# @@ -47,7 +48,7 @@ endif() set(CELERITAS_CMAKE_STRINGS) set(CELERITAS_BUILD_TYPE ${CMAKE_BUILD_TYPE}) -foreach(_var BUILD_TYPE HOSTNAME REAL_TYPE CORE_GEO CORE_RNG UNITS) +foreach(_var BUILD_TYPE HOSTNAME REAL_TYPE UNITS OPENMP CORE_GEO CORE_RNG) set(_var "CELERITAS_${_var}") string(TOLOWER "${_var}" _lower) string(APPEND CELERITAS_CMAKE_STRINGS diff --git a/src/accel/LocalTransporter.cc b/src/accel/LocalTransporter.cc index abdba3f3cd..270b345929 100644 --- a/src/accel/LocalTransporter.cc +++ b/src/accel/LocalTransporter.cc @@ -73,11 +73,12 @@ LocalTransporter::LocalTransporter(SetupOptions const& options, << params.Params()->max_streams() << ")"); // Check that OpenMP and Geant4 threading models don't collide - if (CELERITAS_USE_OPENMP && !celeritas::device() + if (CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK && !celeritas::device() && G4Threading::IsMultithreadedApplication()) { auto msg = CELER_LOG_LOCAL(warning); - msg << "Using multithreaded Geant4 with Celeritas OpenMP"; + msg << "Using multithreaded Geant4 with Celeritas track-level OpenMP " + "parallelism"; if (std::string const& nt_str = celeritas::getenv("OMP_NUM_THREADS"); !nt_str.empty()) { diff --git a/src/celeritas/global/ActionLauncher.hh b/src/celeritas/global/ActionLauncher.hh index 1f20dbc52f..cea3a8d018 100644 --- a/src/celeritas/global/ActionLauncher.hh +++ b/src/celeritas/global/ActionLauncher.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "celeritas_config.h" #include "corecel/Assert.hh" #include "corecel/Types.hh" #include "corecel/sys/MultiExceptionHandler.hh" @@ -34,7 +35,7 @@ void launch_action(ExplicitActionInterface const& action, F&& execute_thread) { MultiExceptionHandler capture_exception; -#ifdef _OPENMP +#if defined(_OPENMP) && CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK # pragma omp parallel for #endif for (size_type i = 0; i < num_threads; ++i) diff --git a/src/celeritas/track/detail/TrackSortUtils.cc b/src/celeritas/track/detail/TrackSortUtils.cc index eda09cb2b7..3a4d13a849 100644 --- a/src/celeritas/track/detail/TrackSortUtils.cc +++ b/src/celeritas/track/detail/TrackSortUtils.cc @@ -114,7 +114,9 @@ void count_tracks_per_action( std::fill(offsets.begin(), offsets.end(), ThreadId{}); auto const size = states.size(); // if get_action(0) != get_action(1), get_action(0) never gets initialized -#pragma omp parallel for +#if CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK +# pragma omp parallel for +#endif for (size_type i = 1; i < size; ++i) { ActionId current_action = get_action(ThreadId{i}); diff --git a/src/celeritas/user/detail/SimpleCaloImpl.cc b/src/celeritas/user/detail/SimpleCaloImpl.cc index 54e52d4001..0549a27fe2 100644 --- a/src/celeritas/user/detail/SimpleCaloImpl.cc +++ b/src/celeritas/user/detail/SimpleCaloImpl.cc @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #include "SimpleCaloImpl.hh" +#include "celeritas_config.h" #include "corecel/Types.hh" #include "corecel/sys/MultiExceptionHandler.hh" #include "corecel/sys/ThreadId.hh" @@ -27,7 +28,9 @@ void simple_calo_accum(HostRef const& step, CELER_EXPECT(step && calo); MultiExceptionHandler capture_exception; SimpleCaloExecutor execute{step, calo}; -#pragma omp parallel for +#if CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK +# pragma omp parallel for +#endif for (ThreadId::size_type i = 0; i < step.size(); ++i) { CELER_TRY_HANDLE(execute(ThreadId{i}), capture_exception); diff --git a/src/celeritas_config.h.in b/src/celeritas_config.h.in index 81ff973e58..301d392443 100644 --- a/src/celeritas_config.h.in +++ b/src/celeritas_config.h.in @@ -31,6 +31,8 @@ @CELERITAS_UNITS_CONFIG@ +@CELERITAS_OPENMP_CONFIG@ + @CELERITAS_CORE_GEO_CONFIG@ @CELERITAS_CORE_RNG_CONFIG@ diff --git a/src/corecel/math/Atomics.hh b/src/corecel/math/Atomics.hh index 149fb2f944..bbcf0820f8 100644 --- a/src/corecel/math/Atomics.hh +++ b/src/corecel/math/Atomics.hh @@ -19,6 +19,13 @@ namespace celeritas //---------------------------------------------------------------------------// /*! * Add to a value, returning the original value. + * + * Note that on CPU, this assumes the atomic add is being done in with \em + * track-level parallelism rather than \em event-level because these utilities + * are meant for "kernel" code. + * + * \warning Multiple events must not use this function to simultaneously modify + * shared data. */ template CELER_FORCEINLINE_FUNCTION T atomic_add(T* address, T value) @@ -28,7 +35,7 @@ CELER_FORCEINLINE_FUNCTION T atomic_add(T* address, T value) #else CELER_EXPECT(address); T initial; -# ifdef _OPENMP +# if defined(_OPENMP) && CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK # pragma omp atomic capture # endif { @@ -78,7 +85,7 @@ CELER_FORCEINLINE_FUNCTION T atomic_min(T* address, T value) #else CELER_EXPECT(address); T initial; -# ifdef _OPENMP +# if defined(_OPENMP) && CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK # pragma omp atomic capture # endif { @@ -101,7 +108,7 @@ CELER_FORCEINLINE_FUNCTION T atomic_max(T* address, T value) #else CELER_EXPECT(address); T initial; -# ifdef _OPENMP +# if defined(_OPENMP) && CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK # pragma omp atomic capture # endif { diff --git a/src/corecel/sys/MultiExceptionHandler.cc b/src/corecel/sys/MultiExceptionHandler.cc index dcd85145c5..c51f636178 100644 --- a/src/corecel/sys/MultiExceptionHandler.cc +++ b/src/corecel/sys/MultiExceptionHandler.cc @@ -106,7 +106,7 @@ MultiExceptionHandler::~MultiExceptionHandler() */ void MultiExceptionHandler::operator()(std::exception_ptr p) { -#ifdef _OPENMP +#if CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK # pragma omp critical(MultiExceptionHandler) #endif { diff --git a/src/geocel/rasterize/RaytraceImager.t.hh b/src/geocel/rasterize/RaytraceImager.t.hh index 8e0272d571..43c8395f8d 100644 --- a/src/geocel/rasterize/RaytraceImager.t.hh +++ b/src/geocel/rasterize/RaytraceImager.t.hh @@ -137,7 +137,7 @@ void RaytraceImager::launch_raytrace_kernel( size_type const num_threads = geo_states.size(); MultiExceptionHandler capture_exception; -#ifdef _OPENMP +#if CELERITAS_OPENMP == CELERITAS_OPENMP_TRACK # pragma omp parallel for #endif for (size_type i = 0; i < num_threads; ++i) From 50109fe41ae0c030a13e50237ffdbb05fbf595cf Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 7 May 2024 14:41:37 -0400 Subject: [PATCH 39/59] Write celer-sim results as 'null' when they're disabled (#1223) * Add step timer to disable gettime calls * Write 'null' time and track counts when they're disabled --- app/celer-sim/RunnerInput.hh | 2 + app/celer-sim/RunnerOutput.cc | 44 +++++++++++++++------- app/celer-sim/StepTimer.hh | 67 ++++++++++++++++++++++++++++++++++ app/celer-sim/Transporter.cc | 18 +++------ app/celer-sim/Transporter.hh | 3 +- app/celer-sim/simple-driver.py | 11 +++++- 6 files changed, 115 insertions(+), 30 deletions(-) create mode 100644 app/celer-sim/StepTimer.hh diff --git a/app/celer-sim/RunnerInput.hh b/app/celer-sim/RunnerInput.hh index edf32c7cb7..f0c09ab1d1 100644 --- a/app/celer-sim/RunnerInput.hh +++ b/app/celer-sim/RunnerInput.hh @@ -34,6 +34,8 @@ namespace app //---------------------------------------------------------------------------// /*! * Input for a single run. + * + * TODO for v1.0: unify these names */ struct RunnerInput { diff --git a/app/celer-sim/RunnerOutput.cc b/app/celer-sim/RunnerOutput.cc index b354cfdaf4..e32791dfd3 100644 --- a/app/celer-sim/RunnerOutput.cc +++ b/app/celer-sim/RunnerOutput.cc @@ -43,8 +43,6 @@ void RunnerOutput::output(JsonPimpl* j) const #if CELERITAS_USE_JSON using json = nlohmann::json; - auto obj = json::object(); - auto active = json::array(); auto alive = json::array(); auto initializers = json::array(); @@ -73,23 +71,41 @@ void RunnerOutput::output(JsonPimpl* j) const step_times.push_back(event.step_times); } } - obj["_index"] = {"event", "step"}; - obj["active"] = std::move(active); - obj["alive"] = std::move(alive); - obj["initializers"] = std::move(initializers); - obj["num_track_slots"] = std::move(num_track_slots); - obj["num_step_iterations"] = std::move(num_step_iterations); - obj["num_steps"] = std::move(num_steps); - obj["num_aborted"] = std::move(num_aborted); - obj["max_queued"] = std::move(max_queued); - obj["time"] = { + + if (active.empty()) + { + // Track count output is disabled + active = nullptr; + alive = nullptr; + initializers = nullptr; + } + + if (step_times.empty()) + { + // Step time output is disabled + step_times = nullptr; + } + + auto times = json::object({ {"steps", std::move(step_times)}, {"actions", result_.action_times}, {"total", result_.total_time}, {"setup", result_.setup_time}, {"warmup", result_.warmup_time}, - }; - obj["num_streams"] = result_.num_streams; + }); + + auto obj = json::object( + {{"_index", json::array({"event", "step"})}, + {"active", std::move(active)}, + {"alive", std::move(alive)}, + {"initializers", std::move(initializers)}, + {"num_track_slots", std::move(num_track_slots)}, + {"num_step_iterations", std::move(num_step_iterations)}, + {"num_steps", std::move(num_steps)}, + {"num_aborted", std::move(num_aborted)}, + {"max_queued", std::move(max_queued)}, + {"num_streams", result_.num_streams}, + {"time", std::move(times)}}); j->obj = std::move(obj); #else diff --git a/app/celer-sim/StepTimer.hh b/app/celer-sim/StepTimer.hh new file mode 100644 index 0000000000..10951ea38f --- /dev/null +++ b/app/celer-sim/StepTimer.hh @@ -0,0 +1,67 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celer-sim/StepTimer.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/sys/Stopwatch.hh" + +namespace celeritas +{ +namespace app +{ +//---------------------------------------------------------------------------// +/*! + * Optionally append a time at every call. + */ +class StepTimer +{ + public: + //!@{ + //! \name Type aliases + using VecDbl = std::vector; + //!@} + + public: + // Construct with a pointer to the times being appended, possibly null + explicit inline StepTimer(VecDbl* times); + + // If enabled, push back the time and reset the timer + inline void operator()(); + + private: + VecDbl* times_; + Stopwatch get_step_time_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with a pointer to the times being appended. + * + * If this is null, the stopwatch will not be enabled. + */ +StepTimer::StepTimer(VecDbl* times) : times_{times} {} + +//---------------------------------------------------------------------------// +/*! + * Push back the time and reset the timer, if requested. + */ +void StepTimer::operator()() +{ + if (times_) + { + times_->push_back(get_step_time_()); + get_step_time_ = {}; + } +} + +//---------------------------------------------------------------------------// +} // namespace app +} // namespace celeritas diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index e34538158a..1fd36350a4 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -18,7 +18,6 @@ #include "corecel/io/Logger.hh" #include "corecel/io/ScopedTimeLog.hh" #include "corecel/sys/ScopedSignalHandler.hh" -#include "corecel/sys/Stopwatch.hh" #include "celeritas/Types.hh" #include "celeritas/global/CoreParams.hh" #include "celeritas/global/Stepper.hh" @@ -26,6 +25,8 @@ #include "celeritas/grid/VectorUtils.hh" #include "celeritas/phys/Model.hh" +#include "StepTimer.hh" + namespace celeritas { namespace app @@ -113,17 +114,15 @@ auto Transporter::operator()(SpanConstPrimary primaries) CELER_LOG_LOCAL(status) << "Transporting " << primaries.size() << " primaries"; - Stopwatch get_step_time; + StepTimer record_step_time{store_step_times_ ? &result.step_times + : nullptr}; size_type remaining_steps = max_steps_; auto& step = *stepper_; // Copy primaries to device and transport the first step auto track_counts = step(primaries); append_track_counts(track_counts); - if (store_step_times_) - { - result.step_times.push_back(get_step_time()); - } + record_step_time(); while (track_counts) { @@ -141,14 +140,9 @@ auto Transporter::operator()(SpanConstPrimary primaries) break; } - get_step_time = {}; track_counts = step(); - append_track_counts(track_counts); - if (store_step_times_) - { - result.step_times.push_back(get_step_time()); - } + record_step_time(); } result.num_aborted = track_counts.alive + track_counts.queued; diff --git a/app/celer-sim/Transporter.hh b/app/celer-sim/Transporter.hh index af34c93900..1dd9965b43 100644 --- a/app/celer-sim/Transporter.hh +++ b/app/celer-sim/Transporter.hh @@ -60,14 +60,13 @@ struct TransporterInput */ struct TransporterResult { - using VecReal = std::vector; using VecCount = std::vector; // Per-step diagnostics VecCount initializers; //!< Num starting track initializers VecCount active; //!< Num tracks active at beginning of step VecCount alive; //!< Num living tracks at end of step - VecReal step_times; //!< Real time per step + std::vector step_times; //!< Real time per step // Always-on basic diagnostics size_type num_track_slots{}; //!< Number of total track slots diff --git a/app/celer-sim/simple-driver.py b/app/celer-sim/simple-driver.py index 81446a7149..f2b91658eb 100755 --- a/app/celer-sim/simple-driver.py +++ b/app/celer-sim/simple-driver.py @@ -95,6 +95,7 @@ def strtobool(text): 'action_diagnostic': True, 'step_diagnostic': True, 'step_diagnostic_bins': 200, + 'write_step_times': use_device, 'simple_calo': simple_calo, 'sync': True, 'merge_events': False, @@ -141,6 +142,12 @@ def strtobool(text): json.dump(j, f, indent=1) print("Results written to", outfilename, file=stderr) -time = j['result']['runner']['time'].copy() -time.pop('steps') +run_output =j['result']['runner'] +time = run_output['time'].copy() +steps = time.pop('steps') +if use_device: + assert(len(steps) == run_output['num_step_iterations'][0]) +else: + # Step times disabled on CPU from input + assert(steps is None) print(json.dumps(time, indent=1)) From 87a3a37d781aae6d768abcccf3dc8fa6ca57588d Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 8 May 2024 06:24:26 -0400 Subject: [PATCH 40/59] Refactor trapezoid construction (#1221) * Fix coplanarity test for single precision * Simplify trapezoid construction * Fix phi restriction * Use Turn for both theta and phi and refactor * Fix quantity operator * Define angles more clearly --- src/corecel/math/Quantity.hh | 4 +- src/orange/g4org/SolidConverter.cc | 153 ++++++++++-------- src/orange/orangeinp/ConvexRegion.cc | 121 +++++++------- src/orange/orangeinp/ConvexRegion.hh | 35 ++-- src/orange/orangeinp/detail/PolygonUtils.hh | 26 +-- test/orange/g4org/SolidConverter.test.cc | 66 ++++---- test/orange/orangeinp/ConvexRegion.test.cc | 34 +++- .../orangeinp/detail/PolygonUtils.test.cc | 8 - 8 files changed, 238 insertions(+), 209 deletions(-) diff --git a/src/corecel/math/Quantity.hh b/src/corecel/math/Quantity.hh index 5a6502b059..1fbf8a4dfa 100644 --- a/src/corecel/math/Quantity.hh +++ b/src/corecel/math/Quantity.hh @@ -201,8 +201,8 @@ CELER_CONSTEXPR_FUNCTION auto operator*(Quantity lhs, T2 rhs) noexcept return Quantity>{lhs.value() * rhs}; } -template -CELER_CONSTEXPR_FUNCTION auto operator*(T rhs, Quantity lhs) noexcept +template +CELER_CONSTEXPR_FUNCTION auto operator*(T rhs, Quantity lhs) noexcept -> decltype(auto) { return Quantity>{rhs * lhs.value()}; diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index 145c5ccc86..f8202de71c 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -49,6 +49,7 @@ #include "corecel/cont/Range.hh" #include "corecel/io/Logger.hh" #include "corecel/math/Algorithms.hh" +#include "corecel/math/ArraySoftUnit.hh" #include "corecel/math/SoftEqual.hh" #include "corecel/sys/TypeDemangler.hh" #include "orange/orangeinp/CsgObject.hh" @@ -67,17 +68,6 @@ namespace g4org { namespace { -//---------------------------------------------------------------------------// -/*! - * Construct a shape using the solid's name and forwarded arguments. - */ -template -auto make_shape(G4VSolid const& solid, Args&&... args) -{ - return std::make_shared>(std::string{solid.GetName()}, - CR{std::forward(args)...}); -} - //---------------------------------------------------------------------------// /*! * Get the enclosed azimuthal angle by a solid. @@ -105,6 +95,65 @@ SolidEnclosedAngle get_polar_wedge(S const& solid) native_value_to(solid.GetDeltaThetaAngle())}; } +//---------------------------------------------------------------------------// +/*! + * Return theta, phi angles for a G4Para or G4Trap given their symmetry axis. + * + * Certain Geant4 shapes are constructed by skewing the z axis and providing + * the polar/azimuthal angle of the transformed axis. This calculates that + * transform by converting from cartesian to spherical coordinates. + * + * The components of the symmetry axis for G4Para/Trap are always encoded as a + * vector + * \f$ (\mu \tan(\theta)\cos(\phi), \mu \tan(\theta)\sin(\phi), \mu) \f$. + */ +[[maybe_unused]] auto to_polar(G4ThreeVector const& axis) + -> std::pair +{ + CELER_EXPECT(axis.z() > 0); + CELER_EXPECT( + is_soft_unit_vector(Array{axis.x(), axis.y(), axis.z()})); + + double const theta = 1 / std::cos(axis.z()); + double const phi = std::atan2(axis.x() / axis.z(), axis.y() / axis.z()); + + return {native_value_to(theta), native_value_to(phi)}; +} + +//---------------------------------------------------------------------------// +/*! + * Return theta, phi angles for a G4Para or G4Trap given their symmetry axis. + * + * Certain Geant4 shapes are constructed by skewing the z axis and providing + * the polar/azimuthal angle of the transformed axis. This calculates that + * transform by converting from cartesian to spherical coordinates. + * + * The components of the symmetry axis for G4Para/Trap are always encoded as a + * vector \f$ (A \tan(\theta)\cos(\phi), A \tan(\theta)\sin(phi), A) \f$. + */ +template +auto calculate_theta_phi(S const& solid) -> std::pair +{ +#if G4VERSION_NUMBER >= 1100 + double const theta = solid.GetTheta(); + double const phi = solid.GetPhi(); + return {native_value_to(theta), native_value_to(phi)}; +#else + return to_polar(solid.GetSymAxis()); +#endif +} + +//---------------------------------------------------------------------------// +/*! + * Construct a shape using the solid's name and forwarded arguments. + */ +template +auto make_shape(G4VSolid const& solid, Args&&... args) +{ + return std::make_shared>(std::string{solid.GetName()}, + CR{std::forward(args)...}); +} + //---------------------------------------------------------------------------// /*! * Construct an ORANGE solid using the G4Solid's name and forwarded arguments. @@ -343,8 +392,8 @@ auto SolidConverter::generictrap(arg_type solid_base) -> result_type std::vector lower(4), upper(4); for (auto i : range(4)) { - lower[i] = scale_.to(vtx[i].x(), vtx[i].y()); - upper[i] = scale_.to(vtx[i + 4].x(), vtx[i + 4].y()); + lower[i] = scale_.to(vtx[i].x(), vtx[i].y()); + upper[i] = scale_.to(vtx[i + 4].x(), vtx[i + 4].y()); } real_type hh = scale_(solid.GetZHalfLength()); @@ -384,23 +433,15 @@ auto SolidConverter::orb(arg_type solid_base) -> result_type auto SolidConverter::para(arg_type solid_base) -> result_type { auto const& solid = dynamic_cast(solid_base); -#if G4VERSION_NUMBER >= 1100 - double const theta = solid.GetTheta(); - double const phi = solid.GetPhi(); -#else - // TODO: instead of duplicating with g4vg - CELER_NOT_IMPLEMENTED("older Geant4 for ORANGE conversion"); - double const theta = 0; - double const phi = 0; -#endif + auto const [theta, phi] = calculate_theta_phi(solid); return make_shape( solid, scale_.to(solid.GetXHalfLength(), solid.GetYHalfLength(), solid.GetZHalfLength()), native_value_to(std::atan(solid.GetTanAlpha())), - native_value_to(theta), - native_value_to(phi)); + theta, + phi); } //---------------------------------------------------------------------------// @@ -583,21 +624,6 @@ auto SolidConverter::torus(arg_type solid_base) -> result_type /*! * Convert a trapezoid. * - * Here is the full description of the G4Trap parameters, as named here: - * - * hz - Half Z length - distance from the origin to the bases - * hy1 - Half Y length of the base at -pDz - * hy2 - Half Y length of the base at +pDz - * hx1 - Half X length at smaller Y of the base at -pDz - * hx2 - Half X length at bigger Y of the base at -pDz - * hx3 - Half X length at smaller Y of the base at +pDz - * hx4 - Half X length at bigger y of the base at +pDz - * Theta - Polar angle of the line joining the centres of the bases at -/+hz - * Phi - Azimuthal angle of the line joining the centre of the base at -hz - * to the centre of the base at +hz - * Alph1 - Angle between the Y-axis and the centre line of the base at -hz - * Alph2 - Angle between the Y-axis and the centre line of the base at +hz - * * Note that the numbers of x,y,z parameters in the G4Trap are related to the * fact that the two z-faces are parallel (separated by hz) and the 4 x-wedges * (2 in each z-face) are also parallel (separated by hy1,2). @@ -609,40 +635,31 @@ auto SolidConverter::trap(arg_type solid_base) -> result_type { auto const& solid = dynamic_cast(solid_base); - real_type tan_theta{}; - Turn phi{}; -#if G4VERSION_NUMBER < 1100 - // Geant4 10.7 and earlier - axis = (sinth.cosphi, sinth.sinphi, costh) - // Note: both sinth,costh >= 0 since theta is in [0, pi/2] for a G4Trap - auto axis = solid.GetSymAxis(); - auto sin_theta = std::max(0.0, std::sqrt(1.0 - ipow<2>(axis.z()))); - tan_theta = SoftZero{1.e-8}(axis.z()) - ? sin_theta / axis.z() - : numeric_limits::infinity(); - real_type cos_phi = sin_theta > 0 ? axis.x() / sin_theta : 1.0; - real_type sin_phi = sin_theta > 0 ? axis.y() / sin_theta : 1.0; - phi = native_value_to(std::atan2(sin_phi, cos_phi)); + auto const [theta, phi] = calculate_theta_phi(solid); +#if G4VERSION_NUMBER >= 1100 + double const alpha_1 = solid.GetAlpha1(); + double const alpha_2 = solid.GetAlpha2(); #else - // Geant4 11 and later - tan_theta = std::tan(solid.GetTheta()); - phi = native_value_to(solid.GetPhi()); + double const alpha_1 = std::atan(solid.GetTanAlpha1()); + double const alpha_2 = std::atan(solid.GetTanAlpha2()); #endif auto hz = scale_(solid.GetZHalfLength()); - auto hy1 = scale_(solid.GetYHalfLength1()); - auto hx1 = scale_(solid.GetXHalfLength1()); - auto hx2 = scale_(solid.GetXHalfLength2()); - auto hy2 = scale_(solid.GetYHalfLength2()); - auto hx3 = scale_(solid.GetXHalfLength3()); - auto hx4 = scale_(solid.GetXHalfLength4()); - return make_shape( - solid, - GenTrap::from_trap(hz, - tan_theta, - phi, - {hy1, hx1, hx2, solid.GetTanAlpha1()}, - {hy2, hx3, hx4, solid.GetTanAlpha2()})); + GenTrap::TrapFace lo; + lo.hy = scale_(solid.GetYHalfLength1()); + lo.hx_lo = scale_(solid.GetXHalfLength1()); + lo.hx_hi = scale_(solid.GetXHalfLength2()); + lo.tan_alpha = alpha_1; + + GenTrap::TrapFace hi; + hi.hy = scale_(solid.GetYHalfLength2()); + hi.hx_lo = scale_(solid.GetXHalfLength3()); + hi.hx_hi = scale_(solid.GetXHalfLength4()); + hi.tan_alpha = alpha_2; + + return make_shape(solid, + GenTrap::from_trap(hz, theta, phi, lo, hi)); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/ConvexRegion.cc index 75c62222fa..b60a542d8e 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/ConvexRegion.cc @@ -12,6 +12,7 @@ #include "corecel/Constants.hh" #include "corecel/cont/Range.hh" #include "corecel/io/JsonPimpl.hh" +#include "corecel/io/Repr.hh" #include "corecel/math/SoftEqual.hh" #include "geocel/BoundingBox.hh" #include "geocel/Types.hh" @@ -316,8 +317,7 @@ void Ellipsoid::output(JsonPimpl* j) const // GENTRAP //---------------------------------------------------------------------------// /*! - * Construct a TRD from 5 half-lengths: one half-height, {hx1,hy1} for a in -z, - * and {hx2,hy2} in +z + * Construct from two simple, centered trapezoids. */ GenTrap GenTrap::from_trd(real_type halfz, Real2 const& lo, Real2 const& hi) { @@ -338,56 +338,63 @@ GenTrap GenTrap::from_trd(real_type halfz, Real2 const& lo, Real2 const& hi) //---------------------------------------------------------------------------// /*! - * Construct from half Z height and 1-4 vertices for top and bottom planes. + * Construct from skewed trapezoids. + * + * For details on construction, see: + * https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html#constructed-solid-geometry-csg-solids + * + * \arg hz Half the distance between the faces + * \arg theta Polar angle of line between center of bases + * \arg phi Azimuthal angle of line between center of bases + * \arg lo Trapezoidal face at -hz + * \arg lo Trapezoidal face at +hz */ -GenTrap GenTrap::from_trap(real_type hz, - real_type tan_theta, - Turn const& phi, - TrapFace const& lo, - TrapFace const& hi) +GenTrap GenTrap::from_trap( + real_type hz, Turn theta, Turn phi, TrapFace const& lo, TrapFace const& hi) { - // TrapFace is validated in its constructor CELER_VALIDATE(hz > 0, << "nonpositive half-height: " << hz); - - CELER_VALIDATE(tan_theta >= 0, << "negative tan(theta): " << tan_theta); - + CELER_VALIDATE(theta >= zero_quantity() && theta < Turn{0.25}, + << "invalid angle " << theta.value() + << " [turns]: must be in the range [0, 0.25)"); CELER_VALIDATE(phi >= zero_quantity() && phi < Turn{1.}, << "invalid angle " << phi.value() << " [turns]: must be in the range [0, 1)"); - for (TrapFace const* tf : {&lo, &hi}) + // Calculate offset of faces from z axis + auto [dxdz_hz, dydz_hz] = [&]() -> std::pair { + real_type cos_phi{}, sin_phi{}; + sincos(phi, &sin_phi, &cos_phi); + real_type const tan_theta = std::tan(native_value_from(theta)); + return {hz * tan_theta * cos_phi, hz * tan_theta * sin_phi}; + }(); + + // Construct points on faces + TrapFace const* const faces[] = {&lo, &hi}; + Array points; + for (auto i : range(2)) { - CELER_VALIDATE(tf->hx_lo_ > 0, - << "nonpositive lower x half-edge: " << tf->hx_lo_); - CELER_VALIDATE(tf->hx_hi_ > 0, - << "nonpositive upper x half-edge: " << tf->hx_hi_); - CELER_VALIDATE(tf->hy_ > 0, - << "nonpositive y half-distance: " << tf->hy_); + TrapFace const& face = *faces[i]; + CELER_VALIDATE(face.hx_lo > 0, + << "nonpositive lower x half-edge: " << face.hx_lo); + CELER_VALIDATE(face.hx_hi > 0, + << "nonpositive upper x half-edge: " << face.hx_hi); + CELER_VALIDATE(face.hy > 0, + << "nonpositive y half-distance: " << face.hy); + CELER_VALIDATE(!std::isinf(face.tan_alpha), + << "infinite trapezoidal shear: " << face.tan_alpha); + + real_type const xoff = (i == 0 ? -dxdz_hz : dxdz_hz); + real_type const yoff = (i == 0 ? -dydz_hz : dydz_hz); + real_type const shear = face.tan_alpha * face.hy; + + // Construct points counterclockwise from lower left + points[i] = {{xoff - shear - face.hx_lo, yoff - face.hy}, + {xoff - shear + face.hx_lo, yoff - face.hy}, + {xoff + shear + face.hx_hi, yoff + face.hy}, + {xoff + shear - face.hx_hi, yoff + face.hy}}; } - real_type cos_phi{}, sin_phi{}; - sincos(phi, &sin_phi, &cos_phi); - auto dxdz_hz = tan_theta * cos_phi * hz; - auto dydz_hz = tan_theta * sin_phi * hz; - - auto hy1 = lo.hy_; - auto hx1 = lo.hx_lo_; - auto hx2 = lo.hx_hi_; - auto hy2 = hi.hy_; - auto hx3 = hi.hx_lo_; - auto hx4 = hi.hx_hi_; - auto dxdy_hy1 = lo.tan_alpha_ * lo.hy_; - auto dxdy_hy2 = hi.tan_alpha_ * hi.hy_; - VecReal2 lower = {{-dxdz_hz - dxdy_hy1 - hx1, -dydz_hz - hy1}, - {-dxdz_hz - dxdy_hy1 + hx1, -dydz_hz - hy1}, - {-dxdz_hz + dxdy_hy1 + hx2, -dydz_hz + hy1}, - {-dxdz_hz + dxdy_hy1 - hx2, -dydz_hz + hy1}}; - VecReal2 upper = {{+dxdz_hz - dxdy_hy2 - hx3, +dydz_hz - hy2}, - {+dxdz_hz - dxdy_hy2 + hx3, +dydz_hz - hy2}, - {+dxdz_hz + dxdy_hy2 + hx4, +dydz_hz + hy2}, - {+dxdz_hz + dxdy_hy2 - hx4, +dydz_hz + hy2}}; - - return GenTrap{hz, std::move(lower), std::move(upper)}; + return GenTrap{hz, std::move(points[0]), std::move(points[1])}; } //---------------------------------------------------------------------------// @@ -431,16 +438,22 @@ GenTrap::GenTrap(real_type halfz, VecReal2 const& lo, VecReal2 const& hi) for (auto i : range(lo_.size())) { auto j = (i + 1) % lo_.size(); - Real3 const a{lo_[i][0], lo_[i][1], -hz_}; - Real3 const b{lo_[j][0], lo_[j][1], -hz_}; - Real3 const c{hi_[j][0], hi_[j][1], hz_}; - Real3 const d{hi_[i][0], hi_[i][1], hz_}; + Real3 const ilo{lo_[i][0], lo_[i][1], -hz_}; + Real3 const jlo{lo_[j][0], lo_[j][1], -hz_}; + Real3 const jhi{hi_[j][0], hi_[j][1], hz_}; + Real3 const ihi{hi_[i][0], hi_[i][1], hz_}; + + // Calculate outward normal by taking the cross product of the edges + auto lo_normal = make_unit_vector(cross_product(jlo - ilo, ihi - ilo)); + auto hi_normal = make_unit_vector(cross_product(ihi - jhi, jlo - jhi)); // *Temporarily* throws if a side face is not planar - if (!detail::is_planar(a, b, c, d)) - { - CELER_NOT_IMPLEMENTED("non-planar side faces"); - } + CELER_VALIDATE( + soft_equal(dot_product(lo_normal, hi_normal), real_type{1}), + << "non-planar face " << i << " on GenTrap: lower left normal is " + << repr(lo_normal) << " and upper right normal is " + << repr(hi_normal) << ": coordinates are lo = " << repr(lo_) + << ", hi = " << repr(hi)); } } @@ -462,16 +475,10 @@ void GenTrap::build(ConvexSurfaceBuilder& insert_surface) const auto j = (i + 1) % lo_.size(); Real3 const ilo{lo_[i][0], lo_[i][1], -hz_}; Real3 const jlo{lo_[j][0], lo_[j][1], -hz_}; - Real3 const jhi{hi_[j][0], hi_[j][1], hz_}; Real3 const ihi{hi_[i][0], hi_[i][1], hz_}; // Calculate outward normal by taking the cross product of the edges auto normal = make_unit_vector(cross_product(jlo - ilo, ihi - ilo)); - // Assert that the surface is (for now!) not twisted - CELER_ASSERT(soft_equal( - dot_product(make_unit_vector(cross_product(ihi - jhi, jlo - jhi)), - normal), - real_type{1})); // Insert the plane insert_surface(Sense::inside, Plane{normal, ilo}); @@ -554,11 +561,9 @@ Parallelepiped::Parallelepiped(Real3 const& half_projs, CELER_VALIDATE(alpha_ > -Turn{0.25} && alpha_ < Turn{0.25}, << "invalid angle " << alpha_.value() << " [turns]: must be in the range (-0.25, 0.25)"); - CELER_VALIDATE(theta_ >= zero_quantity() && theta_ < Turn{0.25}, << "invalid angle " << theta_.value() << " [turns]: must be in the range [0, 0.25)"); - CELER_VALIDATE(phi_ >= zero_quantity() && phi_ < Turn{1.}, << "invalid angle " << phi_.value() << " [turns]: must be in the range [0, 1)"); diff --git a/src/orange/orangeinp/ConvexRegion.hh b/src/orange/orangeinp/ConvexRegion.hh index db91969844..245bcbb14e 100644 --- a/src/orange/orangeinp/ConvexRegion.hh +++ b/src/orange/orangeinp/ConvexRegion.hh @@ -198,14 +198,14 @@ class Ellipsoid final : public ConvexRegionInterface //---------------------------------------------------------------------------// /*! - * A generalized trapezoid, inspired by VecGeom's GenTrap and also ROOT's Arb8. + * A generalized polygon with flat faces along the z axis. + * + * A GenTrap, like VecGeom's equivalent and ROOT's Arb8, represents a general + * trapezoidal volume with up to eight vertices, or two 4-point sets, sitting + * on two parallel planes perpendicular to Z axis. * - * A GenTrap represents a general trapezoidal volume with up to eight vertices, - * or two 4-point sets, sitting on two parallel planes perpendicular to Z axis. - * The points in each set might be correspondingly ordered, in such a way to - * properly define the side faces. - * TODO: Add a check for this. * TODO: Add proper treatment for degenerate cases. + * TODO: support twisted faces. */ class GenTrap final : public ConvexRegionInterface { @@ -216,18 +216,17 @@ class GenTrap final : public ConvexRegionInterface using VecReal2 = std::vector; //!@} - //! Argument for building from regular trapezoidal top/bottom polygons + //! Regular trapezoidal top/bottom face struct TrapFace { - real_type hy_{}; //!< half the vertical distance between horizontal - //!< edges - real_type hx_lo_{}; //!< top horizontal edge half-length - real_type hx_hi_{}; //!< botton horizontal edge half-length - - // tan(alpha), where alpha is the clockwise angle between the - // _centers_ of horizontal edges, with respect to the vertical - // (alpha=0) - real_type tan_alpha_{}; + //! Half the vertical distance between horizontal edges + real_type hy{}; + //! Top horizontal edge half-length + real_type hx_lo{}; + //! Bottom horizontal edge half-length + real_type hx_hi{}; + //! Tangent of shear angle, between horizontal line centers and Y axis + real_type tan_alpha{}; }; public: @@ -238,8 +237,8 @@ class GenTrap final : public ConvexRegionInterface // Helper function to construct a general trap from its half-height and // the two trapezoids defining its lower and upper faces static GenTrap from_trap(real_type hz, - real_type tan_theta, - Turn const& phi, + Turn theta, + Turn phi, TrapFace const& lo, TrapFace const& hi); diff --git a/src/orange/orangeinp/detail/PolygonUtils.hh b/src/orange/orangeinp/detail/PolygonUtils.hh index bed5aa9094..27b1609495 100644 --- a/src/orange/orangeinp/detail/PolygonUtils.hh +++ b/src/orange/orangeinp/detail/PolygonUtils.hh @@ -44,9 +44,8 @@ enum class Orientation /*! * Find orientation of ordered vertices in 2D coordinates. */ -inline CELER_FUNCTION Orientation calc_orientation(Real2 const& a, - Real2 const& b, - Real2 const& c) +inline Orientation +calc_orientation(Real2 const& a, Real2 const& b, Real2 const& c) { auto crossp = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0]); return crossp < 0 ? Orientation::clockwise @@ -60,8 +59,7 @@ inline CELER_FUNCTION Orientation calc_orientation(Real2 const& a, * * The list of input corners must have at least 3 points to be a polygon. */ -inline CELER_FUNCTION bool -has_orientation(Span const& corners, Orientation o) +inline bool has_orientation(Span const& corners, Orientation o) { CELER_EXPECT(corners.size() > 2); for (auto i : range(corners.size())) @@ -81,8 +79,7 @@ has_orientation(Span const& corners, Orientation o) * \param corners the vertices of the polygon * \param degen_ok allow consecutive degenerate points */ -CELER_FUNCTION bool -is_convex(Span const& corners, bool degen_ok = false) +bool is_convex(Span const& corners, bool degen_ok = false) { CELER_EXPECT(!corners.empty()); @@ -111,21 +108,6 @@ is_convex(Span const& corners, bool degen_ok = false) return true; } -//---------------------------------------------------------------------------// -/*! - * Check for coplanarity of four 3D polygon vertices. - */ -CELER_FUNCTION bool -is_planar(Real3 const& a, Real3 const& b, Real3 const& c, Real3 const& d) -{ - // Use the cross_product(last, first) as sign reference - auto norm = make_unit_vector(cross_product(b - a, c - a)); - auto val = dot_product(norm, d - a); - - return SoftZero{ - ::celeritas::detail::SoftEqualTraits::sqrt_prec()}(val); -} - //---------------------------------------------------------------------------// } // namespace detail } // namespace orangeinp diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index 82828f24fd..36dba343d4 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -400,51 +400,59 @@ TEST_F(SolidConverterTest, subtractionsolid) TEST_F(SolidConverterTest, trap_box) { - { - this->build_and_test( - G4Trap("trap_box", 30, 0, 0, 20, 10, 10, 0, 20, 10, 10, 0), - R"json({"_type":"shape","interior":{"_type":"gentrap", + this->build_and_test( + G4Trap("trap_box", 30, 0, 0, 20, 10, 10, 0, 20, 10, 10, 0), + R"json({"_type":"shape","interior":{"_type":"gentrap", "halfheight":3.0, "lower":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]], "upper":[[-1.0,-2.0],[1.0,-2.0],[1.0,2.0],[-1.0,2.0]]}, "label":"trap_box"})json", - {{-1, -2, -3}, {1, 2, 3}, {1, 2, 4}}); - } + {{-1, -2, -3}, {1, 2, 3}, {1, 2, 4}}); } TEST_F(SolidConverterTest, trap_trd) { - { - this->build_and_test( - G4Trap("trap_trd", 50, 100, 100, 200, 300), - R"json({"_type":"shape","interior":{"_type":"gentrap", + this->build_and_test( + G4Trap("trap_trd", 50, 100, 100, 200, 300), + R"json({"_type":"shape","interior":{"_type":"gentrap", "halfheight":30.0, "lower":[[-5.0,-10.0],[5.0,-10.0],[5.0,10.0],[-5.0,10.0]], "upper":[[-10.0,-20.0],[10.0,-20.0],[10.0,20.0],[-10.0,20.0]]}, "label":"trap_trd"})json", - {{-10, -20, -40}, - {-10, -20, -30 + 1.e-6}, - {5, 10, 30}, - {10, 10, 30}}); - } + {{-10, -20, -40}, {-10, -20, -30 + 1.e-6}, {5, 10, 30}, {10, 10, 30}}); } TEST_F(SolidConverterTest, trap) { - this->build_and_test( - G4Trap("trap", 40, 0.02, 0.05, 20, 10, 10, 0.01, 30, 15, 15, 0.01), - R"json({"_type":"shape","interior":{"_type":"gentrap", - "halfheight":4.0, - "lower":[[-1.0999113425658524,-2.0039988667381046], - [0.9000886574341476,-2.0039988667381046], - [0.9400899908208165,1.9960011332618952], - [-1.0599100091791835,1.9960011332618952]], - "upper":[[-1.4500903241674836,-2.9960011332618954], - [1.5499096758325164,-2.9960011332618954], - [1.6099116759125196,3.0039988667381046], - [-1.3900883240874804,3.0039988667381046]]}, - "label":"trap"})json", - {{-1, -2, -4 - 1.e-6}, {-1, -2, -3}, {0.5, 1, 3}, {1, 1, 3}}); + double tan_alpha = std::tan(15 * degree); + this->build_and_test(G4Trap("trap", + 40, + 5 * degree, + 10 * degree, + 20, + 10, + 10, + tan_alpha, + 30, + 15, + 15, + tan_alpha), + R"json({"_type":"shape","interior":{"_type":"gentrap", + "halfheight":4.0, + "lower":[[-1.8805364414262706,-2.060768987951168], + [0.11946355857372937,-2.060768987951168], + [1.1912603282982202,1.9392310120488323], + [-0.8087396717017798,1.9392310120488323]], + "upper":[[-1.9592095207293427,-2.939231012048832], + [1.0407904792706573,-2.939231012048832], + [2.648485633857393,3.060768987951168], + [-0.3515143661426068,3.060768987951168]]}, + "label":"trap"})json", + {{-1.89, -2.1, -4.01}, + {0.12, -2.07, -4.01}, + {1.20, 1.94, -4.01}, + {-0.81, 1.9, -4.01}, + {-1.96, -2.94, 4.01}}); } TEST_F(SolidConverterTest, trd_box) diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 11348aff43..055a849286 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -620,8 +620,8 @@ TEST_F(GenTrapTest, trapezoid_ccw) TEST_F(GenTrapTest, trap_theta) { - auto result = this->test( - GenTrap::from_trap(40, 1, Turn{0}, {20, 10, 10, 0}, {20, 10, 10, 0})); + auto result = this->test(GenTrap::from_trap( + 40, Turn{0.125}, Turn{0}, {20, 10, 10, 0}, {20, 10, 10, 0})); static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; static char const* const expected_surfaces[] @@ -642,7 +642,7 @@ TEST_F(GenTrapTest, trap_theta) TEST_F(GenTrapTest, trap_thetaphi) { auto result = this->test(GenTrap::from_trap( - 40, 1, Turn{0.25}, {20, 10, 10, 0}, {20, 10, 10, 0})); + 40, Turn{0.125}, Turn{0.25}, {20, 10, 10, 0}, {20, 10, 10, 0})); static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; static char const* const expected_surfaces[] @@ -660,10 +660,36 @@ TEST_F(GenTrapTest, trap_thetaphi) EXPECT_VEC_SOFT_EQ((Real3{10, inf, 40}), result.exterior.upper()); } +TEST_F(GenTrapTest, trap_g4) +{ + constexpr Turn degree{real_type{1} / 360}; + real_type tan_alpha = std::tan(15 * native_value_from(degree)); + + auto result = this->test(GenTrap::from_trap(4, + 5 * degree, + 10 * degree, + {2, 1, 1, tan_alpha}, + {3, 1.5, 1.5, tan_alpha})); + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-4", + "Plane: z=4", + "Plane: n={0,0.99403,0.10915}, d=-2.4851", + "Plane: n={0.95664,-0.25633,-0.13832}, d=1.1958", + "Plane: n={0,0.99032,-0.13883}, d=2.4758", + "Plane: n={0.96575,-0.25877,-0.018918}, d=-1.2072"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -4}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 4}), result.exterior.upper()); +} + TEST_F(GenTrapTest, trap_full) { auto result = this->test(GenTrap::from_trap( - 40, 1, Turn{0.125}, {20, 10, 10, 0.1}, {20, 10, 10, 0.1})); + 40, Turn{0.125}, Turn{0.125}, {20, 10, 10, 0.1}, {20, 10, 10, 0.1})); static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; static char const* const expected_surfaces[] diff --git a/test/orange/orangeinp/detail/PolygonUtils.test.cc b/test/orange/orangeinp/detail/PolygonUtils.test.cc index 6f6a6f37ca..6fef2cb6d2 100644 --- a/test/orange/orangeinp/detail/PolygonUtils.test.cc +++ b/test/orange/orangeinp/detail/PolygonUtils.test.cc @@ -102,14 +102,6 @@ TEST(PolygonUtilsTest, self_intersect) EXPECT_FALSE(is_convex(self_int2)); } -TEST(PolygonUtilsTest, planar) -{ - Real3 a{-2, 2, -2}, b{2, 2, -2}, c{2, 2, 2}, d{-2, 2, 2}; - EXPECT_TRUE(is_planar(a, b, c, d)); - EXPECT_TRUE(is_planar(a, b, d, c)); // proper ordering not required - EXPECT_FALSE(is_planar(a, b, c, Real3{0, 0, 0})); -} - //---------------------------------------------------------------------------// } // namespace test } // namespace detail From d8b245cf811b74884928d7b02b57f29181fbe200 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 8 May 2024 16:13:51 -0400 Subject: [PATCH 41/59] Fix trapezoid construction for negative phi and older Geant4 (#1227) * Fix GPU driver test * Add trap with negative y skew * Fix to_polar * Remove unnecessary validate --- app/celer-sim/simple-driver.py | 5 +++-- src/orange/g4org/SolidConverter.cc | 5 ++--- src/orange/orangeinp/ConvexRegion.cc | 4 ---- test/orange/g4org/SolidConverter.test.cc | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/app/celer-sim/simple-driver.py b/app/celer-sim/simple-driver.py index f2b91658eb..9620848fce 100755 --- a/app/celer-sim/simple-driver.py +++ b/app/celer-sim/simple-driver.py @@ -146,8 +146,9 @@ def strtobool(text): time = run_output['time'].copy() steps = time.pop('steps') if use_device: - assert(len(steps) == run_output['num_step_iterations'][0]) + assert steps + assert len(steps[0]) == run_output['num_step_iterations'][0] else: # Step times disabled on CPU from input - assert(steps is None) + assert steps is None print(json.dumps(time, indent=1)) diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index f8202de71c..6b4b1f8964 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -114,9 +114,8 @@ SolidEnclosedAngle get_polar_wedge(S const& solid) CELER_EXPECT( is_soft_unit_vector(Array{axis.x(), axis.y(), axis.z()})); - double const theta = 1 / std::cos(axis.z()); - double const phi = std::atan2(axis.x() / axis.z(), axis.y() / axis.z()); - + double const theta = std::acos(axis.z()); + double const phi = std::atan2(axis.y(), axis.x()); return {native_value_to(theta), native_value_to(phi)}; } diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/ConvexRegion.cc index b60a542d8e..0b67bc21ce 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/ConvexRegion.cc @@ -35,7 +35,6 @@ namespace orangeinp { namespace { -using Real2 = Array; //---------------------------------------------------------------------------// /*! * Create a z-aligned bounding box infinite along z and symmetric in r. @@ -356,9 +355,6 @@ GenTrap GenTrap::from_trap( CELER_VALIDATE(theta >= zero_quantity() && theta < Turn{0.25}, << "invalid angle " << theta.value() << " [turns]: must be in the range [0, 0.25)"); - CELER_VALIDATE(phi >= zero_quantity() && phi < Turn{1.}, - << "invalid angle " << phi.value() - << " [turns]: must be in the range [0, 1)"); // Calculate offset of faces from z axis auto [dxdz_hz, dydz_hz] = [&]() -> std::pair { diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index 36dba343d4..b689488ac8 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -453,6 +453,25 @@ TEST_F(SolidConverterTest, trap) {1.20, 1.94, -4.01}, {-0.81, 1.9, -4.01}, {-1.96, -2.94, 4.01}}); + tan_alpha = std::tan(30 * degree); + this->build_and_test( + G4Trap("trap", + 10, + 10 * degree, + -15 * degree, + 20, + 10, + 10, + tan_alpha, + 30, + 15, + 15, + tan_alpha), + R"json({"_type":"shape","interior":{"_type":"gentrap","halfheight":1.0,"lower":[[-2.325019322917132,-1.9543632192272244],[-0.32501932291713187,-1.9543632192272244],[1.9843817538413706,2.0456367807727753],[-0.01561824615862939,2.0456367807727753]],"upper":[[-3.061732023030996,-3.0456367807727753],[-0.06173202303099612,-3.0456367807727753],[3.4023695921067576,2.9543632192272247],[0.4023695921067574,2.9543632192272247]]},"label":"trap"})json", + {{-2.33, -1.96, -1.01}, + {-2.32, -1.95, -0.99}, + {3.41, 2.96, 1.01}, + {3.40, 2.95, 0.99}}); } TEST_F(SolidConverterTest, trd_box) From 8982e7c5bfde928add4953b33eb2f5070029247c Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 10 May 2024 00:05:22 -0400 Subject: [PATCH 42/59] Propagate parent bounding boxes to ensure objects have finite extents (#1225) * Create persistent builders for BIH so we can dedupe later * Add failing bbox test * Fix isinf test and add is_finite * Add BIH test showing failure * Add test demonstrating failure * Add calc_inverse to transforms * Add starting extents to CSG unit builder * Start out convex surface builder with UB's extents * Add bbox storage and expansion to InputBuilder * Build unit protos using bounding box * Update BIH test to prohbit semi-infinite construction * Add validation for finite global exterior * Refactor function to work around msvc bug --- src/orange/BoundingBoxUtils.hh | 22 +- src/orange/detail/BIHBuilder.cc | 64 ++--- src/orange/detail/BIHBuilder.hh | 25 +- src/orange/detail/BIHPartitioner.cc | 7 +- src/orange/orangeinp/ConvexSurfaceBuilder.cc | 4 + src/orange/orangeinp/UnitProto.cc | 46 +++- src/orange/orangeinp/UnitProto.hh | 9 +- src/orange/orangeinp/detail/CsgUnitBuilder.cc | 34 ++- src/orange/orangeinp/detail/CsgUnitBuilder.hh | 15 +- src/orange/orangeinp/detail/InputBuilder.cc | 21 +- src/orange/orangeinp/detail/InputBuilder.hh | 21 ++ src/orange/transform/NoTransformation.hh | 3 + src/orange/transform/Transformation.cc | 9 + src/orange/transform/Transformation.hh | 3 + src/orange/transform/Translation.hh | 5 +- src/orange/transform/VariantTransform.cc | 12 + src/orange/transform/VariantTransform.hh | 6 +- test/orange/BoundingBoxUtils.test.cc | 22 +- test/orange/Orange.test.cc | 12 + .../data/inputbuilder-incomplete-bb.org.json | 236 ++++++++++++++++++ test/orange/detail/BIHBuilder.test.cc | 54 ++-- test/orange/g4org/ProtoConstructor.test.cc | 11 +- test/orange/orangeinp/ConvexRegion.test.cc | 3 +- .../orangeinp/ConvexSurfaceBuilder.test.cc | 21 ++ test/orange/orangeinp/ObjectTestBase.cc | 12 +- test/orange/orangeinp/ObjectTestBase.hh | 4 + test/orange/orangeinp/UnitProto.test.cc | 94 +++++-- test/orange/transform/Transformation.test.cc | 4 + test/orange/transform/Translation.test.cc | 9 + 29 files changed, 666 insertions(+), 122 deletions(-) create mode 100644 test/orange/data/inputbuilder-incomplete-bb.org.json diff --git a/src/orange/BoundingBoxUtils.hh b/src/orange/BoundingBoxUtils.hh index 6f463a9018..44885fbc0f 100644 --- a/src/orange/BoundingBoxUtils.hh +++ b/src/orange/BoundingBoxUtils.hh @@ -27,17 +27,33 @@ class Transformation; //---------------------------------------------------------------------------// /*! * Check if a bounding box spans (-inf, inf) in every direction. + */ +template +inline bool is_infinite(BoundingBox const& bbox) +{ + auto all_equal = [](Array const& values, T rhs) { + return all_of( + values.begin(), values.end(), [rhs](T lhs) { return lhs == rhs; }); + }; + constexpr T inf = numeric_limits::infinity(); + + return all_equal(bbox.lower(), -inf) && all_equal(bbox.upper(), inf); +} + +//---------------------------------------------------------------------------// +/*! + * Check if a bounding box has no infinities. * * \pre The bounding box cannot be null */ template -inline bool is_infinite(BoundingBox const& bbox) +inline bool is_finite(BoundingBox const& bbox) { CELER_EXPECT(bbox); auto isinf = [](T value) { return std::isinf(value); }; - return all_of(bbox.lower().begin(), bbox.lower().end(), isinf) - && all_of(bbox.upper().begin(), bbox.upper().end(), isinf); + return !any_of(bbox.lower().begin(), bbox.lower().end(), isinf) + && !any_of(bbox.upper().begin(), bbox.upper().end(), isinf); } //---------------------------------------------------------------------------// diff --git a/src/orange/detail/BIHBuilder.cc b/src/orange/detail/BIHBuilder.cc index 7ceb1213d1..11f7df964d 100644 --- a/src/orange/detail/BIHBuilder.cc +++ b/src/orange/detail/BIHBuilder.cc @@ -18,47 +18,57 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Construct from a Storage object + * Construct from a Storage object. */ -BIHBuilder::BIHBuilder(Storage* storage) : storage_(storage) +BIHBuilder::BIHBuilder(Storage* storage) + : bboxes_{&storage->bboxes} + , local_volume_ids_{&storage->local_volume_ids} + , inner_nodes_{&storage->inner_nodes} + , leaf_nodes_{&storage->leaf_nodes} { - CELER_EXPECT(storage_); + CELER_EXPECT(storage); } //---------------------------------------------------------------------------// /*! * Create BIH Nodes. */ -BIHTree BIHBuilder::operator()(VecBBox bboxes) +BIHTree BIHBuilder::operator()(VecBBox&& bboxes) { CELER_EXPECT(!bboxes.empty()); // Store bounding boxes and their corresponding centers - bboxes_ = std::move(bboxes); - centers_.resize(bboxes_.size()); - std::transform(bboxes_.begin(), - bboxes_.end(), - centers_.begin(), + temp_.bboxes = std::move(bboxes); + temp_.centers.resize(temp_.bboxes.size()); + std::transform(temp_.bboxes.begin(), + temp_.bboxes.end(), + temp_.centers.begin(), &celeritas::calc_center); // Separate infinite bounding boxes from finite VecIndices indices; VecIndices inf_volids; - for (auto i : range(bboxes_.size())) + for (auto i : range(temp_.bboxes.size())) { LocalVolumeId id(i); - if (!bboxes_[i]) + if (!temp_.bboxes[i]) { // Null bbox (background volume) is unreachable by volume // initialization } - else if (is_infinite(bboxes_[i])) + else if (is_infinite(temp_.bboxes[i])) { + // Infinite in *every* direction + // TODO: make an exception for "EXTERIOR" volume and remove the + // "infinite volume" exceptions? inf_volids.push_back(id); } else { + // Prohibit semi-infinite bounding boxes because those break the + // cost function + CELER_ASSERT(is_finite(temp_.bboxes[i])); indices.push_back(id); } } @@ -66,11 +76,10 @@ BIHTree BIHBuilder::operator()(VecBBox bboxes) BIHTree tree; tree.bboxes = ItemMap( - make_builder(&storage_->bboxes) - .insert_back(bboxes_.begin(), bboxes_.end())); + bboxes_.insert_back(temp_.bboxes.begin(), temp_.bboxes.end())); - tree.inf_volids = make_builder(&storage_->local_volume_ids) - .insert_back(inf_volids.begin(), inf_volids.end()); + tree.inf_volids + = local_volume_ids_.insert_back(inf_volids.begin(), inf_volids.end()); if (!indices.empty()) { @@ -79,22 +88,19 @@ BIHTree BIHBuilder::operator()(VecBBox bboxes) auto [inner_nodes, leaf_nodes] = this->arrange_nodes(std::move(nodes)); tree.inner_nodes - = make_builder(&storage_->inner_nodes) - .insert_back(inner_nodes.begin(), inner_nodes.end()); + = inner_nodes_.insert_back(inner_nodes.begin(), inner_nodes.end()); tree.leaf_nodes - = make_builder(&storage_->leaf_nodes) - .insert_back(leaf_nodes.begin(), leaf_nodes.end()); + = leaf_nodes_.insert_back(leaf_nodes.begin(), leaf_nodes.end()); } else { // Degenerate case where all bounding boxes are infinite. Create a // single empty leaf node, so that the existence of leaf nodes does not // need to be checked at runtime. - VecLeafNodes leaf_nodes{1}; - tree.leaf_nodes - = make_builder(&storage_->leaf_nodes) - .insert_back(leaf_nodes.begin(), leaf_nodes.end()); + BIHLeafNode const empty_nodes[] = {{}}; + tree.leaf_nodes = leaf_nodes_.insert_back(std::begin(empty_nodes), + std::end(empty_nodes)); } return tree; @@ -108,14 +114,14 @@ BIHTree BIHBuilder::operator()(VecBBox bboxes) */ void BIHBuilder::construct_tree(VecIndices const& indices, VecNodes* nodes, - BIHNodeId parent) const + BIHNodeId parent) { using Edge = BIHInnerNode::Edge; auto current_index = nodes->size(); nodes->resize(nodes->size() + 1); - BIHPartitioner partition(&bboxes_, ¢ers_); + BIHPartitioner partition(&temp_.bboxes, &temp_.centers); if (auto p = partition(indices)) { @@ -145,8 +151,8 @@ void BIHBuilder::construct_tree(VecIndices const& indices, { BIHLeafNode node; node.parent = parent; - node.vol_ids = make_builder(&storage_->local_volume_ids) - .insert_back(indices.begin(), indices.end()); + node.vol_ids + = local_volume_ids_.insert_back(indices.begin(), indices.end()); CELER_EXPECT(node); (*nodes)[current_index] = node; @@ -157,7 +163,7 @@ void BIHBuilder::construct_tree(VecIndices const& indices, /*! * Separate inner nodes from leaf nodes and renumber accordingly. */ -BIHBuilder::ArrangedNodes BIHBuilder::arrange_nodes(VecNodes nodes) const +BIHBuilder::ArrangedNodes BIHBuilder::arrange_nodes(VecNodes const& nodes) const { VecInnerNodes inner_nodes; VecLeafNodes leaf_nodes; diff --git a/src/orange/detail/BIHBuilder.hh b/src/orange/detail/BIHBuilder.hh index 6f17f2c18f..532df0cc11 100644 --- a/src/orange/detail/BIHBuilder.hh +++ b/src/orange/detail/BIHBuilder.hh @@ -11,6 +11,7 @@ #include #include "corecel/cont/Range.hh" +#include "corecel/data/CollectionBuilder.hh" #include "geocel/BoundingBox.hh" #include "BIHPartitioner.hh" @@ -53,36 +54,42 @@ class BIHBuilder explicit BIHBuilder(Storage* storage); // Create BIH Nodes - BIHTree operator()(VecBBox bboxes); + BIHTree operator()(VecBBox&& bboxes); private: /// TYPES /// using Real3 = Array; - using VecReal3 = std::vector; using VecIndices = std::vector; - using PairVecIndices = std::pair; - using AxesCenters = std::vector>; using VecNodes = std::vector>; using VecInnerNodes = std::vector; using VecLeafNodes = std::vector; using ArrangedNodes = std::pair; + struct Temporaries + { + VecBBox bboxes; + std::vector centers; + }; + //// DATA //// - VecBBox bboxes_; - VecReal3 centers_; - Storage* storage_; + Temporaries temp_; + + CollectionBuilder bboxes_; + CollectionBuilder local_volume_ids_; + CollectionBuilder inner_nodes_; + CollectionBuilder leaf_nodes_; //// HELPER FUNCTIONS //// // Recursively construct BIH nodes for a vector of bbox indices void construct_tree(VecIndices const& indices, VecNodes* nodes, - BIHNodeId parent) const; + BIHNodeId parent); // Seperate nodes into inner and leaf vectors and renumber accordingly - ArrangedNodes arrange_nodes(VecNodes nodes) const; + ArrangedNodes arrange_nodes(VecNodes const& nodes) const; }; //---------------------------------------------------------------------------// diff --git a/src/orange/detail/BIHPartitioner.cc b/src/orange/detail/BIHPartitioner.cc index dccb1ba6d7..9b8fa3d297 100644 --- a/src/orange/detail/BIHPartitioner.cc +++ b/src/orange/detail/BIHPartitioner.cc @@ -53,7 +53,6 @@ BIHPartitioner::Partition BIHPartitioner::operator()(VecIndices const& indices) const { CELER_EXPECT(*this); - CELER_EXPECT(!indices.empty()); Partition best_partition; real_type best_cost = std::numeric_limits::infinity(); @@ -130,7 +129,7 @@ BIHPartitioner::make_partition(VecIndices const& indices, Axis axis, real_type position) const { - CELER_EXPECT(!indices.empty()); + CELER_EXPECT(indices.size() > 1); using Edge = BIHInnerNode::Edge; @@ -152,9 +151,13 @@ BIHPartitioner::make_partition(VecIndices const& indices, } } + CELER_ASSERT(!p.indices[Edge::left].empty()); + CELER_ASSERT(!p.indices[Edge::right].empty()); + p.bboxes[Edge::left] = calc_union(*bboxes_, p.indices[Edge::left]); p.bboxes[Edge::right] = calc_union(*bboxes_, p.indices[Edge::right]); + CELER_ENSURE(p); return p; } diff --git a/src/orange/orangeinp/ConvexSurfaceBuilder.cc b/src/orange/orangeinp/ConvexSurfaceBuilder.cc index 6394bc21de..f8290d5847 100644 --- a/src/orange/orangeinp/ConvexSurfaceBuilder.cc +++ b/src/orange/orangeinp/ConvexSurfaceBuilder.cc @@ -58,6 +58,10 @@ ConvexSurfaceBuilder::ConvexSurfaceBuilder(UnitBuilder* ub, State* state) { CELER_EXPECT(ub_ && state_); CELER_EXPECT(*state_); + + // Truncate the region's global bounding box to the unit's global box + state_->global_bzone.exterior = ub->extents(); + state_->global_bzone.interior = ub->extents(); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/UnitProto.cc b/src/orange/orangeinp/UnitProto.cc index ed2171eaf3..dd681aa8b6 100644 --- a/src/orange/orangeinp/UnitProto.cc +++ b/src/orange/orangeinp/UnitProto.cc @@ -13,6 +13,7 @@ #include "corecel/io/Logger.hh" #include "orange/OrangeData.hh" #include "orange/OrangeInput.hh" +#include "orange/transform/VariantTransform.hh" #include "CsgObject.hh" #include "CsgTree.hh" @@ -96,11 +97,12 @@ auto UnitProto::daughters() const -> VecProto */ void UnitProto::build(InputBuilder& input) const { + // Bounding box should be finite if and only if this is the global universe + CELER_EXPECT((input.next_id() == orange_global_universe) + == !input.bbox(input.next_id())); + // Build CSG unit - auto csg_unit = this->build(input.tol(), - input.next_id() == orange_global_universe - ? ExteriorBoundary::is_global - : ExteriorBoundary::is_daughter); + auto csg_unit = this->build(input.tol(), input.bbox(input.next_id())); CELER_ASSERT(csg_unit); // Get the list of all surfaces actually used @@ -122,6 +124,7 @@ void UnitProto::build(InputBuilder& input) const { result.bbox = bz.interior; } + CELER_ENSURE(is_finite(result.bbox)); } // Save surfaces @@ -230,6 +233,7 @@ void UnitProto::build(InputBuilder& input) const vol_iter->label = {"[EXTERIOR]", input_.label}; ++vol_iter; + BoundingBoxBumper bump_bbox{input.tol()}; for (auto const& d : input_.daughters) { LocalVolumeId const vol_id{ @@ -255,6 +259,13 @@ void UnitProto::build(InputBuilder& input) const auto transform_id = fill->transform_id; CELER_ASSERT(transform_id < csg_unit.transforms.size()); iter->second.transform = csg_unit.transforms[transform_id.get()]; + + // Update bounding box of the daughter universe by inverting the + // daughter-to-parent reference transform and applying it to the + // parent-reference-frame bbox + auto local_bbox = apply_transform(calc_inverse(iter->second.transform), + result.volumes[vol_id.get()].bbox); + input.expand_bbox(iter->second.universe_id, bump_bbox(local_bbox)); } // Save attributes from materials @@ -292,16 +303,19 @@ void UnitProto::build(InputBuilder& input) const * to be deleted (assumed inside, implicit from the parent universe's boundary) * or preserved. */ -auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit +auto UnitProto::build(Tol const& tol, BBox const& bbox) const -> Unit { CELER_EXPECT(tol); + CELER_EXPECT(!bbox || is_finite(bbox)); - CELER_LOG(debug) << "Building " << this->label() << ": " - << input_.daughters.size() << " daughters and " + bool const is_global_universe = !static_cast(bbox); + CELER_LOG(debug) << "Building '" << this->label() << "' inside " << bbox + << ": " << input_.daughters.size() << " daughters and " << input_.materials.size() << " materials..."; detail::CsgUnit result; - detail::CsgUnitBuilder unit_builder(&result, tol); + detail::CsgUnitBuilder unit_builder( + &result, tol, is_global_universe ? BBox::from_infinite() : bbox); auto build_volume = [ub = &unit_builder](ObjectInterface const& obj) { detail::VolumeBuilder vb{ub}; @@ -317,6 +331,19 @@ auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit auto ext_vol = build_volume(NegatedObject("[EXTERIOR]", input_.boundary.interior)); CELER_ASSERT(ext_vol == orange_exterior_volume); + if (is_global_universe) + { + detail::VolumeBuilder vb{&unit_builder}; + auto interior_node = input_.boundary.interior->build(vb); + auto region_iter = result.regions.find(interior_node); + CELER_ASSERT(region_iter != result.regions.end()); + auto const& bz = region_iter->second.bounds; + CELER_VALIDATE(!bz.negated && is_finite(bz.exterior), + << "global boundary must be finite: cannot determine " + "extents of interior '" + << input_.boundary.interior->label() << "' in '" + << this->label() << '\''); + } // Build daughters UniverseId daughter_id{0}; @@ -343,11 +370,12 @@ auto UnitProto::build(Tol const& tol, ExteriorBoundary ext) const -> Unit // Build background fill (optional) result.background = input_.background.fill; - if (ext == ExteriorBoundary::is_daughter) + if (!is_global_universe) { // Replace "exterior" with "False" (i.e. interior with true) NodeId ext_node = result.volumes[ext_vol.unchecked_get()]; auto min_node = replace_down(&result.tree, ext_node, False{}); + // Simplify recursively simplify(&result.tree, min_node); } diff --git a/src/orange/orangeinp/UnitProto.hh b/src/orange/orangeinp/UnitProto.hh index 188ed5c996..1bdfdfbdb7 100644 --- a/src/orange/orangeinp/UnitProto.hh +++ b/src/orange/orangeinp/UnitProto.hh @@ -134,13 +134,6 @@ class UnitProto : public ProtoInterface // True if fully defined explicit inline operator bool() const; }; - - //! Whether to implicitly delete the exterior boundary - enum class ExteriorBoundary : bool - { - is_global, //!< Explicit: bounding object remains - is_daughter //!< Implicit: interior is replaced with "true" - }; //!@} public: @@ -162,7 +155,7 @@ class UnitProto : public ProtoInterface //// HELPER FUNCTIONS //// // Construct a standalone unit for testing and external interface - Unit build(Tol const& tol, ExteriorBoundary ext) const; + Unit build(Tol const& tol, BBox const& bbox) const; private: Input input_; diff --git a/src/orange/orangeinp/detail/CsgUnitBuilder.cc b/src/orange/orangeinp/detail/CsgUnitBuilder.cc index 2e84f7b59a..b1e6851d16 100644 --- a/src/orange/orangeinp/detail/CsgUnitBuilder.cc +++ b/src/orange/orangeinp/detail/CsgUnitBuilder.cc @@ -10,8 +10,10 @@ #include "corecel/io/Join.hh" #include "corecel/io/Logger.hh" #include "corecel/io/StreamableVariant.hh" +#include "orange/OrangeData.hh" #include "orange/transform/TransformIO.hh" #include "orange/transform/TransformSimplifier.hh" + namespace celeritas { namespace orangeinp @@ -20,11 +22,16 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Construct with an empty unit (which doesn't yet have any elements). + * Construct with an empty unit, tolerance settings, and a priori extents. + * + * The unit should have no elements to start with. */ -CsgUnitBuilder::CsgUnitBuilder(CsgUnit* u, Tolerance<> const& tol) +CsgUnitBuilder::CsgUnitBuilder(CsgUnit* u, + Tolerance<> const& tol, + BBox const& extents) : unit_{u} , tol_{tol} + , bbox_{extents} , insert_surface_{&unit_->surfaces, tol} , insert_transform_{&unit_->transforms} { @@ -122,6 +129,29 @@ LocalVolumeId CsgUnitBuilder::insert_volume(NodeId n) return result; } +//---------------------------------------------------------------------------// +/*! + * Fill LocalVolumeId{0} with "exterior" to adjust the interior region. + * + * This should be called to process the exterior volume *immediately* after its + * creation. + */ +void CsgUnitBuilder::fill_exterior() +{ + CELER_EXPECT(unit_->volumes.size() == 1); + static_assert(orange_exterior_volume == LocalVolumeId{0}); + + NodeId n = unit_->volumes[orange_exterior_volume.get()]; + auto iter = unit_->regions.find(n); + CELER_ASSERT(iter != unit_->regions.end()); + CELER_VALIDATE(!iter->second.bounds.negated, + << "exterior volume is inside out"); + + // TODO handle edge case where exterior is the composite of two volumes and + // we need to adjust those volumes' bboxes? + bbox_ = calc_intersection(bbox_, iter->second.bounds.exterior); +} + //---------------------------------------------------------------------------// /*! * Fill a volume node with a material. diff --git a/src/orange/orangeinp/detail/CsgUnitBuilder.hh b/src/orange/orangeinp/detail/CsgUnitBuilder.hh index f8054778c9..edd90827b8 100644 --- a/src/orange/orangeinp/detail/CsgUnitBuilder.hh +++ b/src/orange/orangeinp/detail/CsgUnitBuilder.hh @@ -31,6 +31,10 @@ namespace detail * surface inserter. The input "CSG Unit" must exceed the lifetime of this * builder. * + * The geometry construction tolerance is passed to numerous other classes + * during construction. The input bounding box is used to restrict the maximum + * possible bounding box for any of the child objects. + * * This class is meant to be used by: * - Object builders * - Convex surface builder @@ -46,14 +50,17 @@ class CsgUnitBuilder //!@} public: - // Construct with an empty unit and tolerance settings - CsgUnitBuilder(CsgUnit*, Tol const& tol); + // Construct with an empty unit, tolerance settings, and a priori bbox + CsgUnitBuilder(CsgUnit*, Tol const& tol, BBox const& extents); //// ACCESSORS //// //! Tolerance, needed for surface simplifier Tol const& tol() const { return tol_; } + //! Maximum extents of this unit + BBox const& extents() const { return bbox_; } + // Access a typed surface, needed for clipping with deduplicated surface template inline S const& surface(NodeId) const; @@ -90,6 +97,9 @@ class CsgUnitBuilder // Mark a CSG node as a volume of real space LocalVolumeId insert_volume(NodeId); + // Fill LocalVolumeId{0} with "exterior" to adjust the interior region + void fill_exterior(); + // Fill a volume node with a material void fill_volume(LocalVolumeId, MaterialId); @@ -100,6 +110,7 @@ class CsgUnitBuilder private: CsgUnit* unit_; Tol tol_; + BBox bbox_; LocalSurfaceInserter insert_surface_; TransformInserter insert_transform_; diff --git a/src/orange/orangeinp/detail/InputBuilder.cc b/src/orange/orangeinp/detail/InputBuilder.cc index c2901e83bf..5b3ccc5823 100644 --- a/src/orange/orangeinp/detail/InputBuilder.cc +++ b/src/orange/orangeinp/detail/InputBuilder.cc @@ -7,6 +7,8 @@ //---------------------------------------------------------------------------// #include "InputBuilder.hh" +#include "orange/BoundingBoxUtils.hh" + #include "../ProtoInterface.hh" namespace celeritas @@ -22,8 +24,7 @@ namespace detail InputBuilder::InputBuilder(OrangeInput* inp, Tol const& tol, ProtoMap const& protos) - : inp_{inp}, protos_{protos} - + : inp_{inp}, protos_{protos}, bboxes_{protos_.size()} { CELER_EXPECT(inp_); CELER_EXPECT(tol); @@ -32,6 +33,22 @@ InputBuilder::InputBuilder(OrangeInput* inp, inp_->universes.reserve(protos_.size()); } +//---------------------------------------------------------------------------// +/*! + * Expand the bounding box of a universe. + * + * Creating successive instances of a universe's "parent" volume will expand + * the possible extents of that universe. In SCALE, same universe could be + * "holed" (placed) in different volumes with different bounds, so long as the + * enclosures are within the extents of the child universe. + */ +void InputBuilder::expand_bbox(UniverseId uid, BBox const& local_bbox) +{ + CELER_EXPECT(uid < bboxes_.size()); + BBox& target = bboxes_[uid.get()]; + target = calc_union(target, local_bbox); +} + //---------------------------------------------------------------------------// /*! * Add a universe to the input. diff --git a/src/orange/orangeinp/detail/InputBuilder.hh b/src/orange/orangeinp/detail/InputBuilder.hh index 5d7914a375..3428a083d1 100644 --- a/src/orange/orangeinp/detail/InputBuilder.hh +++ b/src/orange/orangeinp/detail/InputBuilder.hh @@ -31,6 +31,10 @@ namespace detail * This is passed to \c ProtoInterface::build. It acts like a two-way map * between universe IDs and pointers to Proto interfaces. It \em must not * exceed the lifetime of any of the protos. + * + * The bounding box for a universe starts as "null" and is expanded by the + * universes that use it: this allows, for example, different masked components + * of an array to be used in multiple universes. */ class InputBuilder { @@ -53,12 +57,19 @@ class InputBuilder //! Get the next universe ID UniverseId next_id() const { return UniverseId(inp_->universes.size()); } + // Get the bounding box of a universe + inline BBox const& bbox(UniverseId) const; + + // Expand the bounding box of a universe + void expand_bbox(UniverseId, BBox const& local_box); + // Construct a universe (to be called *once* per proto) void insert(VariantUniverseInput&& unit); private: OrangeInput* inp_; ProtoMap const& protos_; + std::vector bboxes_; }; //---------------------------------------------------------------------------// @@ -72,6 +83,16 @@ UniverseId InputBuilder::find_universe_id(ProtoInterface const* p) const return protos_.find(p); } +//---------------------------------------------------------------------------// +/*! + * Get the bounding box of a universe. + */ +BBox const& InputBuilder::bbox(UniverseId uid) const +{ + CELER_EXPECT(uid < bboxes_.size()); + return bboxes_[uid.get()]; +} + //---------------------------------------------------------------------------// } // namespace detail } // namespace orangeinp diff --git a/src/orange/transform/NoTransformation.hh b/src/orange/transform/NoTransformation.hh index dc844c2193..82f1e6187a 100644 --- a/src/orange/transform/NoTransformation.hh +++ b/src/orange/transform/NoTransformation.hh @@ -65,6 +65,9 @@ class NoTransformation //! Rotate from parent to daughter (identity) CELER_FUNCTION Real3 const& rotate_down(Real3 const& d) const { return d; } + + // Calculate the inverse during preprocessing + inline NoTransformation calc_inverse() const { return {}; } }; //---------------------------------------------------------------------------// diff --git a/src/orange/transform/Transformation.cc b/src/orange/transform/Transformation.cc index 5db47399ed..f97cf3aa11 100644 --- a/src/orange/transform/Transformation.cc +++ b/src/orange/transform/Transformation.cc @@ -63,5 +63,14 @@ Transformation::Transformation(Translation const& tr) { } +//---------------------------------------------------------------------------// +/*! + * Calculate the inverse during preprocessing. + */ +Transformation Transformation::calc_inverse() const +{ + return Transformation::from_inverse(rot_, tra_); +} + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/orange/transform/Transformation.hh b/src/orange/transform/Transformation.hh index 150d811bbb..090a27f255 100644 --- a/src/orange/transform/Transformation.hh +++ b/src/orange/transform/Transformation.hh @@ -114,6 +114,9 @@ class Transformation [[nodiscard]] inline CELER_FUNCTION Real3 rotate_down(Real3 const& parent_dir) const; + // Calculate the inverse during preprocessing + Transformation calc_inverse() const; + private: Mat3 rot_; Real3 tra_; diff --git a/src/orange/transform/Translation.hh b/src/orange/transform/Translation.hh index 309e5601e4..e7e911e726 100644 --- a/src/orange/transform/Translation.hh +++ b/src/orange/transform/Translation.hh @@ -69,9 +69,12 @@ class Translation // Rotate from daughter to parent (identity) inline CELER_FUNCTION Real3 const& rotate_up(Real3 const& d) const; - //! Rotate from parent to daughter (identity) + // Rotate from parent to daughter (identity) inline CELER_FUNCTION Real3 const& rotate_down(Real3 const& d) const; + //! Calculate the inverse during preprocessing + Translation calc_inverse() const { return Translation{negate(tra_)}; } + private: Real3 tra_; }; diff --git a/src/orange/transform/VariantTransform.cc b/src/orange/transform/VariantTransform.cc index 8691f3feda..6fab6b9c82 100644 --- a/src/orange/transform/VariantTransform.cc +++ b/src/orange/transform/VariantTransform.cc @@ -97,5 +97,17 @@ apply_transform(VariantTransform const& transform, BBox const& bbox) transform); } +//---------------------------------------------------------------------------// +/*! + * Calculate the inverse of a transform. + */ +[[nodiscard]] VariantTransform calc_inverse(VariantTransform const& transform) +{ + CELER_ASSUME(!transform.valueless_by_exception()); + return std::visit( + [](auto const& t) -> VariantTransform { return t.calc_inverse(); }, + transform); +} + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/orange/transform/VariantTransform.hh b/src/orange/transform/VariantTransform.hh index 1083cc5327..2cc8f384ab 100644 --- a/src/orange/transform/VariantTransform.hh +++ b/src/orange/transform/VariantTransform.hh @@ -27,10 +27,14 @@ class BoundingBox; using VariantTransform = EnumVariant; //---------------------------------------------------------------------------// -// Apply the left "daughter-to-parent" transform to the right. +// Apply the left "daughter-to-parent" transform to the right [[nodiscard]] VariantTransform apply_transform(VariantTransform const& left, VariantTransform const& right); +//---------------------------------------------------------------------------// +// Calculate the inverse of a transform +[[nodiscard]] VariantTransform calc_inverse(VariantTransform const& transform); + //---------------------------------------------------------------------------// // Dispatch "daughter-to-parent" transform to bounding box utilities [[nodiscard]] BoundingBox diff --git a/test/orange/BoundingBoxUtils.test.cc b/test/orange/BoundingBoxUtils.test.cc index ba16f0aa8c..5347c81e99 100644 --- a/test/orange/BoundingBoxUtils.test.cc +++ b/test/orange/BoundingBoxUtils.test.cc @@ -23,18 +23,26 @@ class BoundingBoxUtilsTest : public Test TEST_F(BoundingBoxUtilsTest, is_infinite) { - BBox bbox1 = {{0, 0, 0}, {1, 1, 1}}; - EXPECT_FALSE(is_infinite(bbox1)); + EXPECT_FALSE(is_infinite(BBox{{0, 0, 0}, {1, 1, 1}})); + EXPECT_FALSE(is_infinite(BBox{{0, 0, 0}, {inf, inf, inf}})); + EXPECT_FALSE(is_infinite(BBox{{0, -inf, -inf}, {1, inf, inf}})); + EXPECT_FALSE(is_infinite(BBox{{inf, inf, inf}, {inf, inf, inf}})); - BBox bbox2 = {{0, 0, 0}, {inf, inf, inf}}; - EXPECT_FALSE(is_infinite(bbox2)); + EXPECT_TRUE(is_infinite(BBox{{-inf, -inf, -inf}, {inf, inf, inf}})); +} + +TEST_F(BoundingBoxUtilsTest, is_finite) +{ + EXPECT_TRUE(is_finite(BBox{{0, 0, 0}, {1, 1, 1}})); + EXPECT_FALSE(is_finite(BBox{{0, 0, 0}, {inf, inf, inf}})); + EXPECT_FALSE(is_finite(BBox{{0, -inf, -inf}, {1, inf, inf}})); + EXPECT_FALSE(is_finite(BBox{{inf, inf, inf}, {inf, inf, inf}})); - BBox bbox3 = {{-inf, -inf, -inf}, {inf, inf, inf}}; - EXPECT_TRUE(is_infinite(bbox3)); + EXPECT_FALSE(is_finite(BBox{{-inf, -inf, -inf}, {inf, inf, inf}})); if (CELERITAS_DEBUG) { - EXPECT_THROW(is_infinite(BBox{}), DebugError); + EXPECT_THROW(is_finite(BBox{}), DebugError); } } diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index 16e2e6b3aa..e19178fedc 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -1423,6 +1423,18 @@ TEST_F(InputBuilderTest, hierarchy) } } +//---------------------------------------------------------------------------// +TEST_F(InputBuilderTest, incomplete_bb) +{ + if (CELERITAS_USE_JSON) + { + OrangeParamsOutput out(this->geometry()); + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":2,"max_faces":6,"max_intersections":6,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":6,"inner_nodes":1,"leaf_nodes":3,"local_volume_ids":6},"connectivity_records":8,"daughters":1,"local_surface_ids":10,"local_volume_ids":4,"logic_ints":38,"real_ids":8,"reals":26,"rect_arrays":0,"simple_units":2,"surface_types":8,"transforms":1,"universe_indices":2,"universe_types":2,"volume_records":6}})json", + to_string(out)); + } +} + //---------------------------------------------------------------------------// class TestEm3GeantTest : public GeantOrangeTest { diff --git a/test/orange/data/inputbuilder-incomplete-bb.org.json b/test/orange/data/inputbuilder-incomplete-bb.org.json new file mode 100644 index 0000000000..0f73fcaadc --- /dev/null +++ b/test/orange/data/inputbuilder-incomplete-bb.org.json @@ -0,0 +1,236 @@ +{ +"_format": "ORANGE", +"_version": 0, +"tol": { +"abs": 1e-05, +"rel": 1e-05 +}, +"universes": [ +{ +"_type": "unit", +"bbox": [ +[ +-8.660254037844386, +-8.660254037844386, +-8.660254037844386 +], +[ +8.660254037844386, +8.660254037844386, +8.660254037844386 +] +], +"daughters": [ +1 +], +"md": { +"name": "global" +}, +"parent_cells": [ +1 +], +"surface_labels": [ +"[EXTERIOR]", +"bound@s" +], +"surfaces": { +"data": [ +100.0, +2.0, +0.0, +0.0, +25.0 +], +"sizes": [ +1, +4 +], +"types": [ +"sc", +"s" +] +}, +"transforms": [ +[ +2.0, +0.0, +0.0 +] +], +"volume_labels": [ +"[EXTERIOR]@global", +"inner@global", +"shell" +], +"volumes": [ +{ +"faces": [ +0 +], +"logic": "0" +}, +{ +"bbox": [ +[ +-3.0, +-5.0, +-5.0 +], +[ +7.0, +5.0, +5.0 +] +], +"faces": [ +1 +], +"logic": "0 ~" +}, +{ +"bbox": [ +[ +-10.0, +-10.0, +-10.0 +], +[ +10.0, +10.0, +10.0 +] +], +"faces": [ +0, +1 +], +"logic": "0 ~ 1 &" +} +] +}, +{ +"_type": "unit", +"bbox": [ +[ +-4.330127018922193, +-4.330127018922193, +-4.330127018922193 +], +[ +4.330127018922193, +4.330127018922193, +4.330127018922193 +] +], +"md": { +"name": "inner" +}, +"surface_labels": [ +"turd@mz", +"turd@pz", +"turd@p0", +"turd@p1", +"turd@p2", +"turd@p3" +], +"surfaces": { +"data": [ +-3.0, +3.0, +0.0, +0.9863939238321437, +0.1643989873053573, +-1.4795908857482156, +0.9863939238321437, +0.0, +-0.1643989873053573, +1.4795908857482156, +0.0, +0.9863939238321437, +-0.1643989873053573, +1.4795908857482156, +0.9863939238321437, +0.0, +0.1643989873053573, +-1.4795908857482156 +], +"sizes": [ +1, +1, +4, +4, +4, +4 +], +"types": [ +"pz", +"pz", +"p", +"p", +"p", +"p" +] +}, +"volume_labels": [ +"[EXTERIOR]@inner", +"fill", +"turd" +], +"volumes": [ +{ +"faces": [], +"flags": 2, +"logic": "* ~", +"zorder": "x" +}, +{ +"bbox": [ +[ +-5.0, +-5.0, +-5.0 +], +[ +5.0, +5.0, +5.0 +] +], +"faces": [ +0, +1, +2, +3, +4, +5 +], +"flags": 1, +"logic": "0 1 ~ & 2 & 3 ~ & 4 ~ & 5 & ~" +}, +{ +"bbox": [ +[ +-5.000050000000001, +-5.000050000000001, +-3.0 +], +[ +5.000050000000001, +5.000050000000001, +3.0 +] +], +"faces": [ +0, +1, +2, +3, +4, +5 +], +"logic": "0 1 ~ & 2 & 3 ~ & 4 ~ & 5 &" +} +] +} +] +} \ No newline at end of file diff --git a/test/orange/detail/BIHBuilder.test.cc b/test/orange/detail/BIHBuilder.test.cc index 1699c1a87c..dbe3120780 100644 --- a/test/orange/detail/BIHBuilder.test.cc +++ b/test/orange/detail/BIHBuilder.test.cc @@ -7,6 +7,9 @@ //---------------------------------------------------------------------------// #include "orange/detail/BIHBuilder.hh" +#include +#include + #include "corecel/data/CollectionBuilder.hh" #include "corecel/data/CollectionMirror.hh" #include "orange/detail/BIHData.hh" @@ -14,19 +17,15 @@ #include "celeritas_test.hh" -using BIHBuilder = celeritas::detail::BIHBuilder; -using BIHInnerNode = celeritas::detail::BIHInnerNode; -using BIHLeafNode = celeritas::detail::BIHLeafNode; - namespace celeritas { +namespace detail +{ namespace test { -class BIHBuilderTest : public Test +//---------------------------------------------------------------------------// +class BIHBuilderTest : public ::celeritas::test::Test { - public: - void SetUp() {} - protected: std::vector bboxes_; @@ -80,8 +79,8 @@ TEST_F(BIHBuilderTest, basic) bboxes_.push_back({{0, -1, 0}, {5, 0, 100}}); bboxes_.push_back({{0, -1, 0}, {5, 0, 100}}); - BIHBuilder bih(&storage_); - auto bih_tree = bih(std::move(bboxes_)); + BIHBuilder build(&storage_); + auto bih_tree = build(std::move(bboxes_)); ASSERT_EQ(1, bih_tree.inf_volids.size()); EXPECT_EQ(LocalVolumeId{0}, storage_.local_volume_ids[bih_tree.inf_volids[0]]); @@ -234,8 +233,8 @@ TEST_F(BIHBuilderTest, grid) bboxes_.push_back({{2, 2, 0}, {3, 3, 100}}); bboxes_.push_back({{2, 3, 0}, {3, 4, 100}}); - BIHBuilder bih(&storage_); - auto bih_tree = bih(std::move(bboxes_)); + BIHBuilder build(&storage_); + auto bih_tree = build(std::move(bboxes_)); ASSERT_EQ(1, bih_tree.inf_volids.size()); EXPECT_EQ(LocalVolumeId{0}, storage_.local_volume_ids[bih_tree.inf_volids[0]]); @@ -376,8 +375,8 @@ TEST_F(BIHBuilderTest, single_finite_volume) { bboxes_.push_back({{0, 0, 0}, {1, 1, 1}}); - BIHBuilder bih(&storage_); - auto bih_tree = bih(std::move(bboxes_)); + BIHBuilder build(&storage_); + auto bih_tree = build(std::move(bboxes_)); ASSERT_EQ(0, bih_tree.inf_volids.size()); ASSERT_EQ(0, bih_tree.inner_nodes.size()); @@ -394,8 +393,8 @@ TEST_F(BIHBuilderTest, multiple_nonpartitionable_volumes) bboxes_.push_back({{0, 0, 0}, {1, 1, 1}}); bboxes_.push_back({{0, 0, 0}, {1, 1, 1}}); - BIHBuilder bih(&storage_); - auto bih_tree = bih(std::move(bboxes_)); + BIHBuilder build(&storage_); + auto bih_tree = build(std::move(bboxes_)); ASSERT_EQ(0, bih_tree.inf_volids.size()); ASSERT_EQ(0, bih_tree.inner_nodes.size()); @@ -412,8 +411,8 @@ TEST_F(BIHBuilderTest, single_infinite_volume) { bboxes_.push_back(FastBBox::from_infinite()); - BIHBuilder bih(&storage_); - auto bih_tree = bih(std::move(bboxes_)); + BIHBuilder build(&storage_); + auto bih_tree = build(std::move(bboxes_)); ASSERT_EQ(0, bih_tree.inner_nodes.size()); ASSERT_EQ(1, bih_tree.leaf_nodes.size()); @@ -428,8 +427,8 @@ TEST_F(BIHBuilderTest, multiple_infinite_volumes) bboxes_.push_back(FastBBox::from_infinite()); bboxes_.push_back(FastBBox::from_infinite()); - BIHBuilder bih(&storage_); - auto bih_tree = bih(std::move(bboxes_)); + BIHBuilder build(&storage_); + auto bih_tree = build(std::move(bboxes_)); ASSERT_EQ(0, bih_tree.inner_nodes.size()); ASSERT_EQ(1, bih_tree.leaf_nodes.size()); @@ -441,6 +440,21 @@ TEST_F(BIHBuilderTest, multiple_infinite_volumes) storage_.local_volume_ids[bih_tree.inf_volids[1]]); } +TEST_F(BIHBuilderTest, TEST_IF_CELERITAS_DEBUG(semi_finite_volumes)) +{ + constexpr auto inff = std::numeric_limits::infinity(); + bboxes_.push_back(FastBBox{{0, 0, -inff}, {1, 1, inff}}); + bboxes_.push_back(FastBBox{{1, 0, -inff}, {2, 1, inff}}); + bboxes_.push_back(FastBBox{{2, 0, -inff}, {4, 1, inff}}); + bboxes_.push_back(FastBBox{{4, 0, -inff}, {8, 1, inff}}); + bboxes_.push_back(FastBBox{{0, -inff, -inff}, {1, inff, inff}}); + bboxes_.push_back(FastBBox{{-inff, 0, 0}, {inff, 1, 1}}); + + BIHBuilder build(&storage_); + EXPECT_THROW(build(std::move(bboxes_)), DebugError); +} + //---------------------------------------------------------------------------// } // namespace test +} // namespace detail } // namespace celeritas diff --git a/test/orange/g4org/ProtoConstructor.test.cc b/test/orange/g4org/ProtoConstructor.test.cc index 778ce2540d..d8f511d02a 100644 --- a/test/orange/g4org/ProtoConstructor.test.cc +++ b/test/orange/g4org/ProtoConstructor.test.cc @@ -19,9 +19,6 @@ using namespace celeritas::orangeinp::test; using celeritas::orangeinp::UnitProto; using celeritas::orangeinp::detail::ProtoMap; -using ExteriorBoundary = celeritas::orangeinp::UnitProto::ExteriorBoundary; - -#include "celeritas_test.hh" namespace celeritas { @@ -70,13 +67,13 @@ class ProtoConstructorTest : public ::celeritas::test::Test auto build_unit(ProtoMap const& protos, UniverseId id) const { - CELER_EXPECT(id); + CELER_EXPECT(id < protos.size()); auto const* proto = dynamic_cast(protos.at(id)); CELER_ASSERT(proto); return proto->build(tol_, - id == UniverseId{0} - ? ExteriorBoundary::is_global - : ExteriorBoundary::is_daughter); + id == UniverseId{0} ? BBox{} + : BBox{{-1000, -1000, -1000}, + {1000, 1000, 1000}}); } void TearDown() final { ::celeritas::reset_geant_geometry(); } diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 055a849286..8e333301c7 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -69,7 +69,8 @@ class ConvexRegionTest : public ::celeritas::test::Test private: Unit unit_; - UnitBuilder unit_builder_{&unit_, Tol::from_relative(1e-4)}; + UnitBuilder unit_builder_{ + &unit_, Tol::from_relative(1e-4), BBox::from_infinite()}; }; //---------------------------------------------------------------------------// diff --git a/test/orange/orangeinp/ConvexSurfaceBuilder.test.cc b/test/orange/orangeinp/ConvexSurfaceBuilder.test.cc index eec94b3935..3223362fc3 100644 --- a/test/orange/orangeinp/ConvexSurfaceBuilder.test.cc +++ b/test/orange/orangeinp/ConvexSurfaceBuilder.test.cc @@ -265,6 +265,27 @@ TEST_F(ConvexSurfaceBuilderTest, transform) } } +TEST_F(ConvexSurfaceBuilderTest, finite_extents) +{ + this->reset(BBox{{-10, -10, -10}, {10, 10, 10}}); + { + SCOPED_TRACE("slab"); + auto css = this->make_state(); + css.object_name = "sl"; + ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + x_slab(build); + + static real_type const expected_local_bz[] = { + -0.5, -inf, -inf, 0.5, inf, inf, -0.5, -inf, -inf, 0.5, inf, inf, 1}; + static real_type const expected_global_bz[] + = {-0.5, -10, -10, 0.5, 10, 10, -0.5, -10, -10, 0.5, 10, 10, 1}; + static int const expected_nodes[] = {2, 4}; + EXPECT_VEC_SOFT_EQ(expected_local_bz, flattened(css.local_bzone)); + EXPECT_VEC_SOFT_EQ(expected_global_bz, flattened(css.global_bzone)); + EXPECT_VEC_EQ(expected_nodes, to_vec_int(css.nodes)); + } +} + //---------------------------------------------------------------------------// } // namespace test } // namespace orangeinp diff --git a/test/orange/orangeinp/ObjectTestBase.cc b/test/orange/orangeinp/ObjectTestBase.cc index 81b3edacd1..e43cad10d2 100644 --- a/test/orange/orangeinp/ObjectTestBase.cc +++ b/test/orange/orangeinp/ObjectTestBase.cc @@ -25,9 +25,19 @@ namespace test * Create a new unit and unit builder. */ void ObjectTestBase::reset() +{ + this->reset(BBox::from_infinite()); +} + +//---------------------------------------------------------------------------// +/*! + * Create a new unit and unit builder with a known maximum extent. + */ +void ObjectTestBase::reset(BBox const& extents) { unit_ = std::make_shared(); - builder_ = std::make_shared(unit_.get(), this->tolerance()); + builder_ = std::make_shared( + unit_.get(), this->tolerance(), extents); } //---------------------------------------------------------------------------// diff --git a/test/orange/orangeinp/ObjectTestBase.hh b/test/orange/orangeinp/ObjectTestBase.hh index e95f90e7b5..5bba7a2c06 100644 --- a/test/orange/orangeinp/ObjectTestBase.hh +++ b/test/orange/orangeinp/ObjectTestBase.hh @@ -9,6 +9,7 @@ #include +#include "geocel/BoundingBox.hh" #include "orange/OrangeTypes.hh" #include "Test.hh" @@ -58,6 +59,9 @@ class ObjectTestBase : public ::celeritas::test::Test // Create a new unit and unit builder void reset(); + // Create a new unit and unit builder with a known maximum extent + void reset(BBox const& extents); + // Construct a volume LocalVolumeId build_volume(ObjectInterface const& s); diff --git a/test/orange/orangeinp/UnitProto.test.cc b/test/orange/orangeinp/UnitProto.test.cc index 43076ce361..27a6e7eba1 100644 --- a/test/orange/orangeinp/UnitProto.test.cc +++ b/test/orange/orangeinp/UnitProto.test.cc @@ -19,7 +19,6 @@ #include "orange/orangeinp/Shape.hh" #include "orange/orangeinp/Transformed.hh" #include "orange/orangeinp/detail/CsgUnit.hh" -#include "orange/orangeinp/detail/InputBuilder.hh" #include "CsgTestUtils.hh" #include "celeritas_test.hh" @@ -35,8 +34,6 @@ namespace test //---------------------------------------------------------------------------// using SPConstObject = std::shared_ptr; using SPConstProto = std::shared_ptr; -inline constexpr auto is_global = UnitProto::ExteriorBoundary::is_global; -inline constexpr auto is_daughter = UnitProto::ExteriorBoundary::is_daughter; //---------------------------------------------------------------------------// // Construction helper functions @@ -127,6 +124,25 @@ class UnitProtoTest : public ::celeritas::test::Test //---------------------------------------------------------------------------// using LeafTest = UnitProtoTest; +TEST_F(LeafTest, errors) +{ + EXPECT_THROW(UnitProto(UnitProto::Input{}), RuntimeError); + + { + SCOPED_TRACE("infinite global box"); + UnitProto::Input inp; + inp.label = "leaf"; + inp.boundary.interior = std::make_shared( + "bad-interior", make_cyl("bound", 1.0, 1.0)); + inp.boundary.zorder = ZOrder::media; + inp.materials.push_back( + make_material(SPConstObject(inp.boundary.interior), 1)); + UnitProto const proto{std::move(inp)}; + + EXPECT_THROW(proto.build(tol_, BBox{}), RuntimeError); + } +} + // All space is explicitly accounted for TEST_F(LeafTest, explicit_exterior) { @@ -143,7 +159,7 @@ TEST_F(LeafTest, explicit_exterior) EXPECT_EQ("", proto_labels(proto.daughters())); { - auto u = proto.build(tol_, is_global); + auto u = proto.build(tol_, BBox{}); static char const* const expected_surface_strings[] = {"Plane: z=-1", "Plane: z=1", "Cyl z: r=1", "Plane: z=0"}; @@ -174,7 +190,7 @@ TEST_F(LeafTest, explicit_exterior) EXPECT_EQ(MaterialId{}, u.background); } { - auto u = proto.build(tol_, is_daughter); + auto u = proto.build(tol_, BBox{{-2, -2, -1}, {2, 2, 1}}); static char const* const expected_volume_strings[] = {"F", "-3", "+3"}; EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); @@ -193,7 +209,7 @@ TEST_F(LeafTest, implicit_exterior) UnitProto const proto{std::move(inp)}; { - auto u = proto.build(tol_, is_global); + auto u = proto.build(tol_, BBox{}); static char const* const expected_surface_strings[] = { "Plane: z=-1", @@ -213,7 +229,7 @@ TEST_F(LeafTest, implicit_exterior) EXPECT_EQ(MaterialId{0}, u.background); } { - auto u = proto.build(tol_, is_daughter); + auto u = proto.build(tol_, BBox{{-2, -2, -1}, {2, 2, 1}}); static char const* const expected_volume_strings[] = {"F", "all(+3, -4)"}; @@ -259,7 +275,7 @@ TEST_F(MotherTest, explicit_exterior) EXPECT_EQ("d1,d2", proto_labels(proto.daughters())); { - auto u = proto.build(tol_, is_global); + auto u = proto.build(tol_, BBox{}); static char const* const expected_surface_strings[] = { "Sphere: r=10", @@ -321,7 +337,7 @@ TEST_F(MotherTest, explicit_exterior) EXPECT_EQ(MaterialId{}, u.background); } { - auto u = proto.build(tol_, is_daughter); + auto u = proto.build(tol_, BBox{{-10, -10, -10}, {10, 10, 10}}); static char const* const expected_volume_strings[] = {"F", "-1", "-2", "-3", "-4", "all(+1, +2, +3, +4)"}; EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); @@ -349,7 +365,7 @@ TEST_F(MotherTest, implicit_exterior) EXPECT_EQ("d1,d2", proto_labels(proto.daughters())); { - auto u = proto.build(tol_, is_global); + auto u = proto.build(tol_, BBox{}); static char const* const expected_volume_strings[] = {"+0", "-1", "-2", "-3", "-4"}; static int const expected_volume_nodes[] = {2, 5, 7, 9, 11}; @@ -359,7 +375,7 @@ TEST_F(MotherTest, implicit_exterior) EXPECT_EQ(MaterialId{3}, u.background); } { - auto u = proto.build(tol_, is_daughter); + auto u = proto.build(tol_, BBox{{-10, -10, -10}, {10, 10, 10}}); static char const* const expected_volume_strings[] = {"F", "-1", "-2", "-3", "-4"}; EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); @@ -384,7 +400,7 @@ TEST_F(MotherTest, fuzziness) EXPECT_EQ("d1", proto_labels(proto.daughters())); { - auto u = proto.build(tol_, is_global); + auto u = proto.build(tol_, BBox{}); static char const* const expected_surface_strings[] = {"Sphere: r=10", "Sphere: r=1", "Sphere: r=1.0001"}; static char const* const expected_volume_strings[] @@ -405,7 +421,7 @@ TEST_F(MotherTest, fuzziness) { // Simplify with lower tolerance because the user has tried to avoid // overlap by adding .0001 to the "similar" shape - auto u = proto.build(Tol::from_relative(1e-3), is_global); + auto u = proto.build(Tol::from_relative(1e-3), BBox{}); static char const* const expected_volume_strings[] = {"+0", "-1", "all(-0, +1)"}; EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); @@ -416,7 +432,7 @@ TEST_F(MotherTest, fuzziness) class InputBuilderTest : public UnitProtoTest { public: - void test(UnitProto const& global) + void run_test(UnitProto const& global) { OrangeInput inp = build_input(tol_, global); EXPECT_TRUE(inp); @@ -474,7 +490,7 @@ TEST_F(InputBuilderTest, globalspheres) return inp; }()}; - this->test(global); + this->run_test(global); } TEST_F(InputBuilderTest, bgspheres) @@ -492,7 +508,7 @@ TEST_F(InputBuilderTest, bgspheres) return inp; }()}; - this->test(global); + this->run_test(global); } // Equivalent to universes.org.omn @@ -558,7 +574,7 @@ TEST_F(InputBuilderTest, universes) return inp; }()); - this->test(*outer); + this->run_test(*outer); } TEST_F(InputBuilderTest, hierarchy) @@ -629,7 +645,49 @@ TEST_F(InputBuilderTest, hierarchy) return inp; }()); - this->test(*global); + this->run_test(*global); +} + +// Equivalent to universes.org.omn +TEST_F(InputBuilderTest, incomplete_bb) +{ + auto inner = std::make_shared([] { + UnitProto::Input inp; + inp.boundary.interior = make_sph("bound", 5.0); + inp.boundary.zorder = ZOrder::media; + inp.label = "inner"; + + using VR2 = GenTrap::VecReal2; + auto trd = make_shape("turd", + real_type{3}, + VR2{{-1, -1}, {1, -1}, {1, 1}, {-1, 1}}, + VR2{{-2, -2}, {2, -2}, {2, 2}, {-2, 2}}); + inp.materials.push_back( + make_material(make_rdv("fill", + {{Sense::inside, inp.boundary.interior}, + {Sense::outside, trd}}), + 1)); + inp.materials.push_back(make_material(std::move(trd), 2)); + return inp; + }()); + + auto outer = std::make_shared([&] { + UnitProto::Input inp; + inp.boundary.interior = make_sph("bound", 10.0); + inp.boundary.zorder = ZOrder::media; + inp.label = "global"; + + inp.daughters.push_back({inner, Translation{{2, 0, 0}}}); + + inp.materials.push_back(make_material( + make_rdv("shell", + {{Sense::inside, inp.boundary.interior}, + {Sense::outside, inp.daughters.front().make_interior()}}), + 1)); + return inp; + }()); + + this->run_test(*outer); } //---------------------------------------------------------------------------// diff --git a/test/orange/transform/Transformation.test.cc b/test/orange/transform/Transformation.test.cc index a04a20d963..255cff96cc 100644 --- a/test/orange/transform/Transformation.test.cc +++ b/test/orange/transform/Transformation.test.cc @@ -71,6 +71,10 @@ TEST_F(TransformationTest, construction) EXPECT_VEC_SOFT_EQ( (Real3{2, -4, 0.1}), trinv.transform_down(tr.transform_down(Real3{2, -4, 0.1}))); + + auto trinv2 = tr.calc_inverse(); + EXPECT_EQ(trinv.translation(), trinv2.translation()); + EXPECT_EQ(trinv.rotation(), trinv2.rotation()); } } diff --git a/test/orange/transform/Translation.test.cc b/test/orange/transform/Translation.test.cc index 2e8c0d8987..01a13082b1 100644 --- a/test/orange/transform/Translation.test.cc +++ b/test/orange/transform/Translation.test.cc @@ -59,6 +59,15 @@ TEST_F(TranslatorTest, serialization) EXPECT_VEC_EQ((Real3{3, 2, 1}), tr2.translation()); } +TEST_F(TranslatorTest, inverse) +{ + Translation tr(Real3{1, 0, 3}); + + auto const inv = tr.calc_inverse(); + EXPECT_VEC_SOFT_EQ((Real3{-1, 0, -3}), inv.translation()); + EXPECT_FALSE(std::signbit(inv.translation()[1])); +} + //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas From bd11e582c5ed0d604d21ed9041a8068090e36616 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Mon, 13 May 2024 07:15:04 -0500 Subject: [PATCH 43/59] Make accel "auto flush" threshold configurable (#1231) --- app/celer-g4/GlobalSetup.cc | 1 + app/celer-g4/RunInput.cc | 3 ++- app/celer-g4/RunInput.hh | 1 + app/celer-g4/RunInputIO.json.cc | 9 +++++++++ src/accel/LocalTransporter.cc | 3 ++- src/accel/SetupOptions.hh | 2 ++ src/accel/SetupOptionsMessenger.cc | 3 +++ src/accel/SetupOptionsMessenger.hh | 1 + 8 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/celer-g4/GlobalSetup.cc b/app/celer-g4/GlobalSetup.cc index 36f4648c28..f616713efb 100644 --- a/app/celer-g4/GlobalSetup.cc +++ b/app/celer-g4/GlobalSetup.cc @@ -167,6 +167,7 @@ void GlobalSetup::ReadInput(std::string const& filename) options_->cuda_heap_size = input_.cuda_heap_size; options_->sync = input_.sync; options_->default_stream = input_.default_stream; + options_->auto_flush = input_.auto_flush; } else if (ends_with(filename, ".mac")) { diff --git a/app/celer-g4/RunInput.cc b/app/celer-g4/RunInput.cc index 157592574b..67b1e74e02 100644 --- a/app/celer-g4/RunInput.cc +++ b/app/celer-g4/RunInput.cc @@ -51,7 +51,8 @@ RunInput::operator bool() const && physics_list < PhysicsListSelection::size_ && (field == no_field() || field_options) && ((num_track_slots > 0 && max_steps > 0 - && initializer_capacity > 0 && secondary_stack_factor > 0) + && initializer_capacity > 0 && secondary_stack_factor > 0 + && auto_flush > 0 && auto_flush <= initializer_capacity) || SharedParams::CeleritasDisabled()) && (step_diagnostic_bins > 0 || !step_diagnostic); } diff --git a/app/celer-g4/RunInput.hh b/app/celer-g4/RunInput.hh index 99f48bc1c6..d8c17adee1 100644 --- a/app/celer-g4/RunInput.hh +++ b/app/celer-g4/RunInput.hh @@ -67,6 +67,7 @@ struct RunInput size_type max_steps{unspecified}; size_type initializer_capacity{}; real_type secondary_stack_factor{}; + size_type auto_flush{}; //!< Defaults to num_track_slots bool sync{false}; bool default_stream{false}; //!< Launch all kernels on the default stream diff --git a/app/celer-g4/RunInputIO.json.cc b/app/celer-g4/RunInputIO.json.cc index b14bcd7f32..10130f2301 100644 --- a/app/celer-g4/RunInputIO.json.cc +++ b/app/celer-g4/RunInputIO.json.cc @@ -72,6 +72,14 @@ void from_json(nlohmann::json const& j, RunInput& v) RI_LOAD_OPTION(secondary_stack_factor); RI_LOAD_OPTION(sync); RI_LOAD_OPTION(default_stream); + if (auto iter = j.find("auto_flush"); iter != j.end()) + { + iter->get_to(v.auto_flush); + } + else + { + v.auto_flush = v.num_track_slots; + } RI_LOAD_OPTION(physics_list); RI_LOAD_OPTION(physics_options); @@ -162,6 +170,7 @@ void to_json(nlohmann::json& j, RunInput const& v) RI_SAVE_OPTION(cuda_heap_size); RI_SAVE(sync); RI_SAVE(default_stream); + RI_SAVE(auto_flush); RI_SAVE(physics_list); if (v.physics_list == PhysicsListSelection::geant_physics_list) diff --git a/src/accel/LocalTransporter.cc b/src/accel/LocalTransporter.cc index 270b345929..52743ed2ba 100644 --- a/src/accel/LocalTransporter.cc +++ b/src/accel/LocalTransporter.cc @@ -50,7 +50,8 @@ namespace celeritas */ LocalTransporter::LocalTransporter(SetupOptions const& options, SharedParams const& params) - : auto_flush_(options.max_num_tracks) + : auto_flush_(options.auto_flush ? options.auto_flush + : options.max_num_tracks) , max_steps_(options.max_steps) , dump_primaries_{params.offload_writer()} , hit_manager_{params.hit_manager()} diff --git a/src/accel/SetupOptions.hh b/src/accel/SetupOptions.hh index 44df17d859..d3d24aa8b2 100644 --- a/src/accel/SetupOptions.hh +++ b/src/accel/SetupOptions.hh @@ -126,6 +126,8 @@ struct SetupOptions size_type initializer_capacity{}; //! At least the average number of secondaries per track slot real_type secondary_stack_factor{3.0}; + //! Number of tracks to buffer before offloading (if unset: max num tracks) + size_type auto_flush{}; //!@} //! Set the number of streams (defaults to run manager # threads) diff --git a/src/accel/SetupOptionsMessenger.cc b/src/accel/SetupOptionsMessenger.cc index 3be3b41ce6..7c51cf226f 100644 --- a/src/accel/SetupOptionsMessenger.cc +++ b/src/accel/SetupOptionsMessenger.cc @@ -190,6 +190,9 @@ SetupOptionsMessenger::SetupOptionsMessenger(SetupOptions* options) add_cmd(&options->secondary_stack_factor, "secondaryStackFactor", "At least the average number of secondaries per track slot"); + add_cmd(&options->auto_flush, + "autoFlush", + "Number of tracks to buffer before offloading"); directories_.emplace_back(new CelerDirectory( "/celer/detector/", "Celeritas sensitive detector setup options")); diff --git a/src/accel/SetupOptionsMessenger.hh b/src/accel/SetupOptionsMessenger.hh index 60344b306e..e4657f65f9 100644 --- a/src/accel/SetupOptionsMessenger.hh +++ b/src/accel/SetupOptionsMessenger.hh @@ -33,6 +33,7 @@ struct SetupOptions; maxNumSteps | Limit on number of step iterations before aborting maxInitializers | Maximum number of track initializers secondaryStackFactor | At least the average number of secondaries per track + autoFlush | Number of tracks to buffer before offloading * The following option is exposed in the \c /celer/detector/ command * "directory": From c72f59d218951b76c8d61655e18e9e0395b991be Mon Sep 17 00:00:00 2001 From: Guilherme Lima Date: Mon, 13 May 2024 07:42:45 -0500 Subject: [PATCH 44/59] Support twisted sides for the gentrap shape (#1232) --- src/orange/orangeinp/ConvexRegion.cc | 58 ++++++++++++---------- test/orange/g4org/SolidConverter.test.cc | 36 +++++++------- test/orange/orangeinp/ConvexRegion.test.cc | 45 ++++++++++++++--- 3 files changed, 87 insertions(+), 52 deletions(-) diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/ConvexRegion.cc index 0b67bc21ce..7cda1cf08a 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/ConvexRegion.cc @@ -429,28 +429,6 @@ GenTrap::GenTrap(real_type halfz, VecReal2 const& lo, VecReal2 const& hi) std::reverse(lo_.begin(), lo_.end()); std::reverse(hi_.begin(), hi_.end()); } - - // TODO: Temporarily ensure that all side faces are planar - for (auto i : range(lo_.size())) - { - auto j = (i + 1) % lo_.size(); - Real3 const ilo{lo_[i][0], lo_[i][1], -hz_}; - Real3 const jlo{lo_[j][0], lo_[j][1], -hz_}; - Real3 const jhi{hi_[j][0], hi_[j][1], hz_}; - Real3 const ihi{hi_[i][0], hi_[i][1], hz_}; - - // Calculate outward normal by taking the cross product of the edges - auto lo_normal = make_unit_vector(cross_product(jlo - ilo, ihi - ilo)); - auto hi_normal = make_unit_vector(cross_product(ihi - jhi, jlo - jhi)); - - // *Temporarily* throws if a side face is not planar - CELER_VALIDATE( - soft_equal(dot_product(lo_normal, hi_normal), real_type{1}), - << "non-planar face " << i << " on GenTrap: lower left normal is " - << repr(lo_normal) << " and upper right normal is " - << repr(hi_normal) << ": coordinates are lo = " << repr(lo_) - << ", hi = " << repr(hi)); - } } //---------------------------------------------------------------------------// @@ -471,13 +449,43 @@ void GenTrap::build(ConvexSurfaceBuilder& insert_surface) const auto j = (i + 1) % lo_.size(); Real3 const ilo{lo_[i][0], lo_[i][1], -hz_}; Real3 const jlo{lo_[j][0], lo_[j][1], -hz_}; + Real3 const jhi{hi_[j][0], hi_[j][1], hz_}; Real3 const ihi{hi_[i][0], hi_[i][1], hz_}; // Calculate outward normal by taking the cross product of the edges - auto normal = make_unit_vector(cross_product(jlo - ilo, ihi - ilo)); + auto lo_normal = make_unit_vector(cross_product(jlo - ilo, ihi - ilo)); + auto hi_normal = make_unit_vector(cross_product(ihi - jhi, jlo - jhi)); - // Insert the plane - insert_surface(Sense::inside, Plane{normal, ilo}); + // Insert appropriate service, planar or "twisted" + if (soft_equal(dot_product(lo_normal, hi_normal), real_type{1})) + { + // Insert a real plane + insert_surface(Sense::inside, Plane{lo_normal, ilo}); + } + else + { + // Determine coefficients + auto alo = jlo[1] - ilo[1]; + auto ahi = jhi[1] - ihi[1]; + auto blo = ilo[0] - jlo[0]; + auto bhi = ihi[0] - jhi[0]; + auto clo = jlo[0] * ilo[1] - ilo[0] * jlo[1]; + auto chi = jhi[0] * ihi[1] - ihi[0] * jhi[1]; + + Real3 abc{0, 0, 0}, def{0, 0, 0}, ghi{0, 0, 0}; + constexpr real_type half = 0.5; + auto factor = half / hz_; + def[0] = (ahi - alo) * factor; + def[1] = (bhi - blo) * factor; + ghi[2] = (chi - clo) * factor; + ghi[0] = (alo + ahi) * half; + ghi[1] = (blo + bhi) * half; + auto offset = half * (clo + chi); + + // Insert a twisted plane + insert_surface(Sense::inside, + GeneralQuadric{abc, def, ghi, offset}); + } } } diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index b689488ac8..c2944e46e6 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -258,25 +258,23 @@ TEST_F(SolidConverterTest, generictrap) } { - // TODO: most generic gentrap with twisted side faces - EXPECT_THROW( - this->build_and_test( - G4GenericTrap("LArEMECInnerWheelAbsorber02", - 10.625, - {{1.55857990922689, 302.468976599716}, - {-1.73031296208306, 302.468976599716}, - {-2.53451906396442, 609.918546236458}, - {2.18738922312177, 609.918546236458}, - {-11.9586196560814, 304.204253530802}, - {-15.2556006134987, 304.204253530802}, - {-31.2774318502685, 613.426120316623}, - {-26.5391748405779, 613.426120316623}}), - R"json({"_type":"shape","interior":{"_type":"gentrap","halfedges":[0.501588152875291,0.5,51.400000000000006],"phi":0.0,"theta":0.22584674950181247},"label":"LArEMECInnerWheelAbsorber02"})json", - { - {51.2, 0.40, 7.76}, - {51.4, 0.51, 7.78}, - }), - celeritas::RuntimeError); + // most general gentrap with twisted side faces + this->build_and_test( + G4GenericTrap("LArEMECInnerWheelAbsorber02", + 10.625, + {{1.55857990922689, 302.468976599716}, + {-1.73031296208306, 302.468976599716}, + {-2.53451906396442, 609.918546236458}, + {2.18738922312177, 609.918546236458}, + {-11.9586196560814, 304.204253530802}, + {-15.2556006134987, 304.204253530802}, + {-31.2774318502685, 613.426120316623}, + {-26.5391748405779, 613.426120316623}}), + R"json({"_type":"shape","interior":{"_type":"gentrap","halfheight":1.0625,"lower":[[0.218738922312177,60.99185462364581],[-0.253451906396442,60.99185462364581],[-0.173031296208306,30.246897659971598],[0.155857990922689,30.246897659971598]],"upper":[[-2.65391748405779,61.342612031662306],[-3.12774318502685,61.342612031662306],[-1.52556006134987,30.420425353080205],[-1.19586196560814,30.420425353080205]]},"label":"LArEMECInnerWheelAbsorber02"})json", + { + {51.2, 0.40, 7.76}, + {51.4, 0.51, 7.78}, + }); } } diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 8e333301c7..ac1ff715a3 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -432,12 +432,6 @@ TEST_F(GenTrapTest, construct) EXPECT_THROW(GenTrap::from_trd(3, {1, -1}, {2, 2}), RuntimeError); // hy1<0 EXPECT_THROW(GenTrap::from_trd(3, {1, 1}, {-2, 2}), RuntimeError); // hx2<0 EXPECT_THROW(GenTrap::from_trd(3, {1, 1}, {2, -2}), RuntimeError); // hy2<0 - - // General non-planar GenTrap with 'twisted' faces is not yet implemented - EXPECT_THROW(GenTrap(3, - {{-10, -10}, {-10, 10}, {10, 10}, {9, -11}}, - {{-10, -10}, {-10, 10}, {10, 10}, {10, -10}}), - RuntimeError); } TEST_F(GenTrapTest, box_like) @@ -724,11 +718,46 @@ TEST_F(GenTrapTest, DISABLED_tetrahedron) } // TODO: find a valid set of points -TEST_F(GenTrapTest, DISABLED_full) +TEST_F(GenTrapTest, full) { auto result = this->test(GenTrap(4, {{-2,-2}, {-2,2}, {2,2}, {2,-2}}, {{-2,-2}, {-1,1}, {1,1}, {2,-2}})); - result.print_expected(); + + static char const expected_node[] = "all(+0, -1, -2, -3, -4, +5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-4", + "Plane: z=4", + "GQuadric: {0,0,0} {-0.125,0.125,0} {3.5,0.5,0.5} -6", + "Plane: n={0,0.99228,0.12403}, d=1.4884", + "GQuadric: {0,0,0} {0.125,0.125,0} {-3.5,0.5,0.5} -6", + "Plane: y=-2"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -2, -4}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 4}), result.exterior.upper()); +} + +TEST_F(GenTrapTest, full2) +{ + auto result = this->test(GenTrap::from_trap( + 40, Turn{0.125}, Turn{0}, {20, 10, 10, 0.1}, {20, 10, 15, -0.2})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, -5)"; + static char const* const expected_surfaces[] + = {"Plane: z=-40", + "Plane: z=40", + "Plane: y=-20", + "GQuadric: {0,0,0} {0,0.0875,0} {40,-0.5,-41.25} -450", + "Plane: y=20", + "GQuadric: {0,0,0} {0,-0.2125,0} {-40,-4.5,38.75} -450"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_FALSE(result.interior) << result.interior; + EXPECT_VEC_SOFT_EQ((Real3{-inf, -20, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, 20, 40}), result.exterior.upper()); } //---------------------------------------------------------------------------// From d6b41aff3ac8d4f1a60b32a7d9c1fff7db27f088 Mon Sep 17 00:00:00 2001 From: Julien Esseiva Date: Tue, 14 May 2024 17:20:33 -0700 Subject: [PATCH 45/59] Improve transport of single track on CPU (#1235) --- app/celer-g4/GlobalSetup.cc | 2 +- app/celer-g4/RunInput.hh | 2 +- app/celer-g4/RunInputIO.json.cc | 7 +++- app/celer-sim/Runner.cc | 2 +- app/celer-sim/RunnerInput.hh | 2 +- app/celer-sim/RunnerInputIO.json.cc | 5 ++- app/celer-sim/Transporter.cc | 8 ++-- app/celer-sim/Transporter.hh | 3 +- app/celer-sim/simple-driver.py | 2 +- src/accel/LocalTransporter.cc | 18 +++++---- src/accel/SetupOptions.hh | 2 +- src/accel/SetupOptionsMessenger.cc | 5 ++- src/celeritas/global/Stepper.cc | 2 +- src/celeritas/global/Stepper.hh | 4 +- src/celeritas/global/detail/ActionSequence.cc | 38 ++++++++++++++----- src/celeritas/global/detail/ActionSequence.hh | 4 +- 16 files changed, 66 insertions(+), 40 deletions(-) diff --git a/app/celer-g4/GlobalSetup.cc b/app/celer-g4/GlobalSetup.cc index f616713efb..442fb77603 100644 --- a/app/celer-g4/GlobalSetup.cc +++ b/app/celer-g4/GlobalSetup.cc @@ -165,7 +165,7 @@ void GlobalSetup::ReadInput(std::string const& filename) options_->sd.enabled = input_.sd_type != SensitiveDetectorType::none; options_->cuda_stack_size = input_.cuda_stack_size; options_->cuda_heap_size = input_.cuda_heap_size; - options_->sync = input_.sync; + options_->action_times = input_.action_times; options_->default_stream = input_.default_stream; options_->auto_flush = input_.auto_flush; } diff --git a/app/celer-g4/RunInput.hh b/app/celer-g4/RunInput.hh index d8c17adee1..9cd844c6cd 100644 --- a/app/celer-g4/RunInput.hh +++ b/app/celer-g4/RunInput.hh @@ -68,7 +68,7 @@ struct RunInput size_type initializer_capacity{}; real_type secondary_stack_factor{}; size_type auto_flush{}; //!< Defaults to num_track_slots - bool sync{false}; + bool action_times{false}; bool default_stream{false}; //!< Launch all kernels on the default stream // Physics setup options diff --git a/app/celer-g4/RunInputIO.json.cc b/app/celer-g4/RunInputIO.json.cc index 10130f2301..b30f5e6d7a 100644 --- a/app/celer-g4/RunInputIO.json.cc +++ b/app/celer-g4/RunInputIO.json.cc @@ -57,6 +57,7 @@ void from_json(nlohmann::json const& j, RunInput& v) { #define RI_LOAD_OPTION(NAME) CELER_JSON_LOAD_OPTION(j, v, NAME) #define RI_LOAD_REQUIRED(NAME) CELER_JSON_LOAD_REQUIRED(j, v, NAME) +#define RI_LOAD_DEPRECATED(OLD, NEW) CELER_JSON_LOAD_DEPRECATED(j, v, OLD, NEW) // Check version (if available) check_format(j, "celer-g4"); @@ -66,11 +67,13 @@ void from_json(nlohmann::json const& j, RunInput& v) RI_LOAD_OPTION(primary_options); + RI_LOAD_DEPRECATED(sync, action_times); + RI_LOAD_OPTION(num_track_slots); RI_LOAD_OPTION(max_steps); RI_LOAD_OPTION(initializer_capacity); RI_LOAD_OPTION(secondary_stack_factor); - RI_LOAD_OPTION(sync); + RI_LOAD_OPTION(action_times); RI_LOAD_OPTION(default_stream); if (auto iter = j.find("auto_flush"); iter != j.end()) { @@ -168,7 +171,7 @@ void to_json(nlohmann::json& j, RunInput const& v) RI_SAVE(secondary_stack_factor); RI_SAVE_OPTION(cuda_stack_size); RI_SAVE_OPTION(cuda_heap_size); - RI_SAVE(sync); + RI_SAVE(action_times); RI_SAVE(default_stream); RI_SAVE(auto_flush); diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 9e86e9bd85..d8f3dd4eeb 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -427,7 +427,7 @@ void Runner::build_transporter_input(RunnerInput const& inp) transporter_input_->max_steps = inp.max_steps; transporter_input_->store_track_counts = inp.write_track_counts; transporter_input_->store_step_times = inp.write_step_times; - transporter_input_->sync = inp.sync; + transporter_input_->action_times = inp.action_times; transporter_input_->params = core_params_; } diff --git a/app/celer-sim/RunnerInput.hh b/app/celer-sim/RunnerInput.hh index f0c09ab1d1..4dbc1e2234 100644 --- a/app/celer-sim/RunnerInput.hh +++ b/app/celer-sim/RunnerInput.hh @@ -72,7 +72,7 @@ struct RunnerInput size_type initializer_capacity{}; //!< Divided among streams real_type secondary_stack_factor{}; bool use_device{}; - bool sync{}; + bool action_times{}; bool merge_events{false}; //!< Run all events at once on a single stream bool default_stream{false}; //!< Launch all kernels on the default stream bool warm_up{CELER_USE_DEVICE}; //!< Run a nullop step first diff --git a/app/celer-sim/RunnerInputIO.json.cc b/app/celer-sim/RunnerInputIO.json.cc index 99878d1861..18786272c9 100644 --- a/app/celer-sim/RunnerInputIO.json.cc +++ b/app/celer-sim/RunnerInputIO.json.cc @@ -93,6 +93,7 @@ void from_json(nlohmann::json const& j, RunnerInput& v) LDIO_LOAD_OPTION(write_step_times); LDIO_LOAD_DEPRECATED(max_num_tracks, num_track_slots); + LDIO_LOAD_DEPRECATED(sync, action_times); LDIO_LOAD_OPTION(seed); LDIO_LOAD_OPTION(num_track_slots); @@ -100,7 +101,7 @@ void from_json(nlohmann::json const& j, RunnerInput& v) LDIO_LOAD_REQUIRED(initializer_capacity); LDIO_LOAD_REQUIRED(secondary_stack_factor); LDIO_LOAD_REQUIRED(use_device); - LDIO_LOAD_OPTION(sync); + LDIO_LOAD_OPTION(action_times); LDIO_LOAD_OPTION(merge_events); LDIO_LOAD_OPTION(default_stream); LDIO_LOAD_OPTION(warm_up); @@ -174,7 +175,7 @@ void to_json(nlohmann::json& j, RunnerInput const& v) LDIO_SAVE(initializer_capacity); LDIO_SAVE(secondary_stack_factor); LDIO_SAVE(use_device); - LDIO_SAVE(sync); + LDIO_SAVE(action_times); LDIO_SAVE(merge_events); LDIO_SAVE(default_stream); LDIO_SAVE(warm_up); diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index 1fd36350a4..2a97f7f22d 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -54,7 +54,7 @@ Transporter::Transporter(TransporterInput inp) step_input.params = inp.params; step_input.num_track_slots = inp.num_track_slots; step_input.stream_id = inp.stream_id; - step_input.sync = inp.sync; + step_input.action_times = inp.action_times; stepper_ = std::make_shared>(std::move(step_input)); } @@ -157,11 +157,11 @@ auto Transporter::operator()(SpanConstPrimary primaries) template void Transporter::accum_action_times(MapStrDouble* result) const { - // Get kernel timing if running with a single stream and if either on the - // device with synchronization enabled or on the host + // Get kernel timing if running with a single stream and if + // synchronization is enabled auto const& step = *stepper_; auto const& action_seq = step.actions(); - if (M == MemSpace::host || action_seq.sync()) + if (action_seq.action_times()) { auto const& action_ptrs = action_seq.actions(); auto const& times = action_seq.accum_time(); diff --git a/app/celer-sim/Transporter.hh b/app/celer-sim/Transporter.hh index 1dd9965b43..9da30e62f7 100644 --- a/app/celer-sim/Transporter.hh +++ b/app/celer-sim/Transporter.hh @@ -38,7 +38,8 @@ struct TransporterInput // Stepper input std::shared_ptr params; size_type num_track_slots{}; //!< AKA max_num_tracks - bool sync{false}; //!< Whether to synchronize device between actions + bool action_times{false}; //!< Whether to synchronize device between + //!< actions for timing // Loop control size_type max_steps{}; diff --git a/app/celer-sim/simple-driver.py b/app/celer-sim/simple-driver.py index 9620848fce..373617b184 100755 --- a/app/celer-sim/simple-driver.py +++ b/app/celer-sim/simple-driver.py @@ -97,7 +97,7 @@ def strtobool(text): 'step_diagnostic_bins': 200, 'write_step_times': use_device, 'simple_calo': simple_calo, - 'sync': True, + 'action_times': True, 'merge_events': False, 'default_stream': False, 'brem_combined': True, diff --git a/src/accel/LocalTransporter.cc b/src/accel/LocalTransporter.cc index 52743ed2ba..6a5d3514f7 100644 --- a/src/accel/LocalTransporter.cc +++ b/src/accel/LocalTransporter.cc @@ -39,6 +39,7 @@ #include "SetupOptions.hh" #include "SharedParams.hh" + #include "detail/HitManager.hh" #include "detail/OffloadWriter.hh" @@ -101,7 +102,7 @@ LocalTransporter::LocalTransporter(SetupOptions const& options, inp.params = params.Params(); inp.stream_id = StreamId{static_cast(thread_id)}; inp.num_track_slots = options.max_num_tracks; - inp.sync = options.sync; + inp.action_times = options.action_times; if (celeritas::device()) { @@ -190,10 +191,12 @@ void LocalTransporter::Flush() { return; } - - CELER_LOG_LOCAL(info) << "Transporting " << buffer_.size() - << " tracks from event " << event_id_.unchecked_get() - << " with Celeritas"; + if (celeritas::device()) + { + CELER_LOG_LOCAL(info) + << "Transporting " << buffer_.size() << " tracks from event " + << event_id_.unchecked_get() << " with Celeritas"; + } if (dump_primaries_) { @@ -254,10 +257,9 @@ auto LocalTransporter::GetActionTime() const -> MapStrReal MapStrReal result; auto const& action_seq = step_->actions(); - if (action_seq.sync() || !celeritas::device()) + if (action_seq.action_times()) { - // Save kernel timing if either on the device with synchronization - // enabled or on the host + // Save kernel timing if synchronization is enabled auto const& action_ptrs = action_seq.actions(); auto const& time = action_seq.accum_time(); diff --git a/src/accel/SetupOptions.hh b/src/accel/SetupOptions.hh index d3d24aa8b2..79c891fb97 100644 --- a/src/accel/SetupOptions.hh +++ b/src/accel/SetupOptions.hh @@ -154,7 +154,7 @@ struct SetupOptions size_type cuda_stack_size{}; size_type cuda_heap_size{}; //! Sync the GPU at every kernel for timing - bool sync{false}; + bool action_times{false}; //! Launch all kernels on the default stream bool default_stream{false}; //!@} diff --git a/src/accel/SetupOptionsMessenger.cc b/src/accel/SetupOptionsMessenger.cc index 7c51cf226f..ffc648598f 100644 --- a/src/accel/SetupOptionsMessenger.cc +++ b/src/accel/SetupOptionsMessenger.cc @@ -210,8 +210,9 @@ SetupOptionsMessenger::SetupOptionsMessenger(SetupOptions* options) add_cmd(&options->cuda_heap_size, "heapSize", "Set the CUDA per-thread heap size for VecGeom"); - add_cmd( - &options->sync, "sync", "Sync the GPU at every kernel for timing"); + add_cmd(&options->action_times, + "actionTimes", + "Add timers around every action (may reduce performance)"); add_cmd(&options->default_stream, "defaultStream", "Launch all kernels on the default stream"); diff --git a/src/celeritas/global/Stepper.cc b/src/celeritas/global/Stepper.cc index 865b3906ef..d6dfb8fb8e 100644 --- a/src/celeritas/global/Stepper.cc +++ b/src/celeritas/global/Stepper.cc @@ -35,7 +35,7 @@ Stepper::Stepper(Input input) // Create action sequence actions_ = [&] { ActionSequence::Options opts; - opts.sync = input.sync; + opts.action_times = input.action_times; return std::make_shared(*params_->action_reg(), opts); }(); diff --git a/src/celeritas/global/Stepper.hh b/src/celeritas/global/Stepper.hh index 60fb8a2a3e..d23144ff89 100644 --- a/src/celeritas/global/Stepper.hh +++ b/src/celeritas/global/Stepper.hh @@ -41,14 +41,14 @@ class ActionSequence; * - \c params : Problem definition * - \c num_track_slots : Maximum number of threads to run in parallel on GPU * \c stream_id : Unique (thread/task) ID for this process - * - \c sync : Whether to synchronize device between actions + * - \c action_times : Whether to synchronize device between actions for timing */ struct StepperInput { std::shared_ptr params; StreamId stream_id{}; size_type num_track_slots{}; - bool sync{false}; + bool action_times{false}; //! True if defined explicit operator bool() const diff --git a/src/celeritas/global/detail/ActionSequence.cc b/src/celeritas/global/detail/ActionSequence.cc index 44d7cec67b..c9813dd5fe 100644 --- a/src/celeritas/global/detail/ActionSequence.cc +++ b/src/celeritas/global/detail/ActionSequence.cc @@ -103,24 +103,39 @@ template void ActionSequence::execute(Params const& params, State& state) { [[maybe_unused]] Stream::StreamT stream = nullptr; - if (M == MemSpace::device && options_.sync) + if (M == MemSpace::device && options_.action_times) { stream = celeritas::device().stream(state.stream_id()).get(); } - if ((M == MemSpace::host || options_.sync) && !state.warming_up()) + // Running a single track slot on host: + // Skip inapplicable post-step action + auto const skip_post_action = [&](auto const& action) { + if constexpr (M != MemSpace::host) + { + return false; + } + return state.size() == 1 && action.order() == ActionOrder::post + && action.action_id() + != state.ref().sim.post_step_action[TrackSlotId{0}]; + }; + + if (options_.action_times && !state.warming_up()) { // Execute all actions and record the time elapsed for (auto i : range(actions_.size())) { - ScopedProfiling profile_this{actions_[i]->label()}; - Stopwatch get_time; - actions_[i]->execute(params, state); - if (M == MemSpace::device) + if (auto const& action = *actions_[i]; !skip_post_action(action)) { - CELER_DEVICE_CALL_PREFIX(StreamSynchronize(stream)); + ScopedProfiling profile_this{action.label()}; + Stopwatch get_time; + action.execute(params, state); + if constexpr (M == MemSpace::device) + { + CELER_DEVICE_CALL_PREFIX(StreamSynchronize(stream)); + } + accum_time_[i] += get_time(); } - accum_time_[i] += get_time(); } } else @@ -128,8 +143,11 @@ void ActionSequence::execute(Params const& params, State& state) // Just loop over the actions for (auto const& sp_action : actions_) { - ScopedProfiling profile_this{sp_action->label()}; - sp_action->execute(params, state); + if (auto const& action = *sp_action; !skip_post_action(action)) + { + ScopedProfiling profile_this{action.label()}; + action.execute(params, state); + } } } } diff --git a/src/celeritas/global/detail/ActionSequence.hh b/src/celeritas/global/detail/ActionSequence.hh index b70373179b..c151b1325a 100644 --- a/src/celeritas/global/detail/ActionSequence.hh +++ b/src/celeritas/global/detail/ActionSequence.hh @@ -60,7 +60,7 @@ class ActionSequence //! Construction/execution options struct Options { - bool sync{false}; //!< Call DeviceSynchronize and add timer + bool action_times{false}; //!< Call DeviceSynchronize and add timer }; public: @@ -80,7 +80,7 @@ class ActionSequence //// ACCESSORS //// //! Whether synchronization is taking place - bool sync() const { return options_.sync; } + bool action_times() const { return options_.action_times; } //! Get the set of beginning-of-run actions VecBeginAction const& begin_run_actions() const { return begin_run_; } From 124e0db275b83238e031820fc43331bb1bb14eb6 Mon Sep 17 00:00:00 2001 From: Hayden Hollenbeck <127582780+hhollenb@users.noreply.github.com> Date: Wed, 15 May 2024 08:10:13 -0400 Subject: [PATCH 46/59] Add generic grid inserter (#1229) --- src/celeritas/grid/GenericGridInserter.hh | 136 +++++++++++++++ src/celeritas/optical/CerenkovParams.cc | 16 +- test/celeritas/CMakeLists.txt | 2 + .../celeritas/grid/GenericGridBuilder.test.cc | 83 +++++++++ .../grid/GenericGridInserter.test.cc | 160 ++++++++++++++++++ 5 files changed, 389 insertions(+), 8 deletions(-) create mode 100644 src/celeritas/grid/GenericGridInserter.hh create mode 100644 test/celeritas/grid/GenericGridBuilder.test.cc create mode 100644 test/celeritas/grid/GenericGridInserter.test.cc diff --git a/src/celeritas/grid/GenericGridInserter.hh b/src/celeritas/grid/GenericGridInserter.hh new file mode 100644 index 0000000000..d308f69e61 --- /dev/null +++ b/src/celeritas/grid/GenericGridInserter.hh @@ -0,0 +1,136 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/grid/GenericGridInserter.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "corecel/Types.hh" +#include "corecel/cont/Span.hh" +#include "corecel/data/Collection.hh" +#include "corecel/data/CollectionBuilder.hh" +#include "celeritas/Types.hh" +#include "celeritas/io/ImportPhysicsVector.hh" + +#include "GenericGridBuilder.hh" +#include "GenericGridData.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct a generic grid using mutable host data and add it to + * the specified grid collection. + * + * \code + GenericGridInserter insert(&data->reals, &data->generic_grids); + std::vector grid_ids; + for (material : range(MaterialId{mats->size()})) + grid_ids.push_back(insert(physics_vector[material.get()])); + \endcode + */ +template +class GenericGridInserter +{ + public: + //!@{ + //! \name Type aliases + using SpanConstFlt = Span; + using SpanConstDbl = Span; + using RealCollection + = Collection; + using GenericGridCollection + = Collection; + //!@} + + public: + // Construct with a reference to mutable host data + GenericGridInserter(RealCollection* real_data, GenericGridCollection* grid); + + // Add a grid of generic data with linear interpolation + Index operator()(SpanConstFlt grid, SpanConstFlt values); + + // Add a grid of generic data with linear interpolation + Index operator()(SpanConstDbl grid, SpanConstDbl values); + + // Add an imported physics vector as a grid + Index operator()(ImportPhysicsVector const& vec); + + // Add an empty grid (no data present) + Index operator()(); + + private: + GenericGridBuilder grid_builder_; + CollectionBuilder grids_; +}; + +//---------------------------------------------------------------------------// +/*! + * Construct with a reference to mutable host data. + */ +template +GenericGridInserter::GenericGridInserter(RealCollection* real_data, + GenericGridCollection* grid) + : grid_builder_(real_data), grids_(grid) +{ + CELER_EXPECT(real_data && grid); +} + +//---------------------------------------------------------------------------// +/*! + * Add an imported physics vector as a generic grid to the collection. + * + * Returns the id of the inserted grid, or an empty id if the vector is + * empty. + */ +template +auto GenericGridInserter::operator()(ImportPhysicsVector const& vec) + -> Index +{ + CELER_EXPECT(!vec.x.empty()); + return grids_.push_back(grid_builder_(vec)); +} + +//---------------------------------------------------------------------------// +/*! + * Add a grid of generic data with linear interpolation to the collection. + */ +template +auto GenericGridInserter::operator()(SpanConstFlt grid, + SpanConstFlt values) -> Index +{ + CELER_EXPECT(!grid.empty()); + return grids_.push_back(grid_builder_(grid, values)); +} + +//---------------------------------------------------------------------------// +/*! + * Add a grid of generic data with linear interpolation to the collection. + */ +template +auto GenericGridInserter::operator()(SpanConstDbl grid, + SpanConstDbl values) -> Index +{ + CELER_EXPECT(!grid.empty()); + return grids_.push_back(grid_builder_(grid, values)); +} + +//---------------------------------------------------------------------------// +/*! + * Add an empty grid. + * + * Useful for when there's no imported grid present for a given material. + */ +template +auto GenericGridInserter::operator()() -> Index +{ + return grids_.push_back({}); +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/optical/CerenkovParams.cc b/src/celeritas/optical/CerenkovParams.cc index 664176e3a5..e4bcb1783e 100644 --- a/src/celeritas/optical/CerenkovParams.cc +++ b/src/celeritas/optical/CerenkovParams.cc @@ -16,8 +16,7 @@ #include "corecel/math/Algorithms.hh" #include "celeritas/Quantities.hh" #include "celeritas/Types.hh" -#include "celeritas/grid/GenericGridBuilder.hh" -#include "celeritas/grid/GenericGridData.hh" +#include "celeritas/grid/GenericGridInserter.hh" #include "OpticalPropertyParams.hh" @@ -33,8 +32,8 @@ CerenkovParams::CerenkovParams(SPConstProperties properties) auto const& host_ref = properties->host_ref(); HostVal data; - GenericGridBuilder build_angle_integral(&data.reals); - CollectionBuilder angle_integral(&data.angle_integral); + GenericGridInserter insert_angle_integral(&data.reals, + &data.angle_integral); for (auto mat_id : range(OpticalMaterialId(host_ref.refractive_index.size()))) @@ -43,7 +42,7 @@ CerenkovParams::CerenkovParams(SPConstProperties properties) if (!ri_grid) { // No refractive index data stored for this material - angle_integral.push_back({}); + insert_angle_integral(); continue; } @@ -58,10 +57,11 @@ CerenkovParams::CerenkovParams(SPConstProperties properties) * (1 / ipow<2>(refractive_index[i - 1]) + 1 / ipow<2>(refractive_index[i])); } - angle_integral.push_back( - build_angle_integral(make_span(energy), make_span(integral))); + + insert_angle_integral(make_span(energy), make_span(integral)); } - CELER_ASSERT(angle_integral.size() == host_ref.refractive_index.size()); + CELER_ASSERT(data.angle_integral.size() + == host_ref.refractive_index.size()); data_ = CollectionMirror{std::move(data)}; CELER_ENSURE(data_ || host_ref.refractive_index.empty()); diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index 8d6e79e99a..dad89d25ee 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -258,6 +258,8 @@ celeritas_add_test(global/Stepper.test.cc # Grid set(CELERITASTEST_PREFIX celeritas/grid) celeritas_add_test(grid/GenericCalculator.test.cc) +celeritas_add_test(grid/GenericGridBuilder.test.cc) +celeritas_add_test(grid/GenericGridInserter.test.cc) celeritas_add_test(grid/GridIdFinder.test.cc) celeritas_add_test(grid/InverseRangeCalculator.test.cc) celeritas_add_test(grid/PolyEvaluator.test.cc) diff --git a/test/celeritas/grid/GenericGridBuilder.test.cc b/test/celeritas/grid/GenericGridBuilder.test.cc new file mode 100644 index 0000000000..1fe9dc532e --- /dev/null +++ b/test/celeritas/grid/GenericGridBuilder.test.cc @@ -0,0 +1,83 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/grid/GenericGridBuilder.test.cc +//---------------------------------------------------------------------------// +#include "celeritas/grid/GenericGridBuilder.hh" + +#include +#include + +#include "celeritas/io/ImportPhysicsVector.hh" + +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +//---------------------------------------------------------------------------// +// TEST HARNESS +//---------------------------------------------------------------------------// + +class GenericGridBuilderTest : public ::celeritas::test::Test +{ + protected: + GenericGridBuilder make_builder() { return GenericGridBuilder(&scalars_); } + + static Span span_grid() { return make_span(grid_); } + + static Span span_values() { return make_span(values_); } + + Collection scalars_; + + constexpr static real_type grid_[] = {0.0, 0.4, 0.9, 1.3}; + constexpr static real_type values_[] = {-31.0, 12.1, 15.5, 92.0}; +}; + +//---------------------------------------------------------------------------// +// TESTS +//---------------------------------------------------------------------------// + +TEST_F(GenericGridBuilderTest, build_span) +{ + auto builder = make_builder(); + + GenericGridData grid_data = builder(span_grid(), span_values()); + + ASSERT_TRUE(grid_data); + ASSERT_EQ(8, scalars_.size()); + ASSERT_EQ(4, grid_data.grid.size()); + ASSERT_EQ(4, grid_data.value.size()); + + EXPECT_VEC_SOFT_EQ(grid_, scalars_[grid_data.grid]); + EXPECT_VEC_SOFT_EQ(values_, scalars_[grid_data.value]); +} + +TEST_F(GenericGridBuilderTest, TEST_IF_CELERITAS_DOUBLE(build_vec)) +{ + ImportPhysicsVector vect; + vect.vector_type = ImportPhysicsVectorType::free; + vect.x = std::vector(span_grid().begin(), span_grid().end()); + vect.y = std::vector(span_values().begin(), span_values().end()); + + auto builder = make_builder(); + + GenericGridData grid_data = builder(vect); + + ASSERT_TRUE(grid_data); + ASSERT_EQ(8, scalars_.size()); + ASSERT_EQ(4, grid_data.grid.size()); + ASSERT_EQ(4, grid_data.value.size()); + + EXPECT_VEC_SOFT_EQ(grid_, scalars_[grid_data.grid]); + EXPECT_VEC_SOFT_EQ(values_, scalars_[grid_data.value]); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/celeritas/grid/GenericGridInserter.test.cc b/test/celeritas/grid/GenericGridInserter.test.cc new file mode 100644 index 0000000000..bb6011bbd9 --- /dev/null +++ b/test/celeritas/grid/GenericGridInserter.test.cc @@ -0,0 +1,160 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/grid/GenericGridInserter.test.cc +//---------------------------------------------------------------------------// +#include "celeritas/grid/GenericGridInserter.hh" + +#include "corecel/OpaqueId.hh" +#include "celeritas/random/distribution/UniformRealDistribution.hh" + +#include "DiagnosticRngEngine.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ + +/* + * Dummy opaque id tag to make sure GenericGridInserter doesn't rely on a + * specific type. + */ +struct GenericIndexTag +{ +}; + +//---------------------------------------------------------------------------// + +class GenericGridInserterTest : public ::celeritas::test::Test +{ + protected: + using GridIndexType = OpaqueId; + using RandomEngine = DiagnosticRngEngine; + + void SetUp() override { rng_.reset_count(); } + + GenericGridInserter make_inserter() + { + return GenericGridInserter(&scalars_, &grids_); + } + + //! Construct an array of random, increasing data to test on + template + std::array build_random_array(real_type start) + { + UniformRealDistribution dist(0.5, 1.5); + std::array xs; + xs[0] = start; + for (size_t i = 1; i < N; i++) + { + xs[i] = xs[i - 1] + dist(rng_); + } + return xs; + } + + //! Check that an inserted grid has been constructed correctly + template + void check_grid(GridIndexType id, + std::array const& xs, + std::array const& ys) const + { + ASSERT_TRUE(id); + ASSERT_LT(id.get(), grids_.size()); + + GenericGridData const& grid = grids_[id]; + ASSERT_EQ(N, grid.grid.size()); + ASSERT_EQ(N, grid.value.size()); + + EXPECT_VEC_EQ(xs, scalars_[grid.grid]); + EXPECT_VEC_EQ(ys, scalars_[grid.value]); + } + + Collection scalars_; + Collection + grids_; + + RandomEngine rng_; +}; + +TEST_F(GenericGridInserterTest, simple) +{ + constexpr size_t count = 105; + auto const xs = build_random_array(-100.0); + auto const ys = build_random_array(300.0); + + auto inserter = make_inserter(); + + GridIndexType grid_index = inserter(make_span(xs), make_span(ys)); + + ASSERT_EQ(1, grids_.size()); + ASSERT_EQ(2 * count, scalars_.size()); + + check_grid(grid_index, xs, ys); +} + +TEST_F(GenericGridInserterTest, many_no_repeats) +{ + constexpr size_t count = 58; + auto inserter = make_inserter(); + + std::vector grid_ids; + std::vector> raw_xs, raw_ys; + + size_t const num_grids = 20; + for (size_t i = 0; i < num_grids; i++) + { + raw_xs.push_back(build_random_array(-100.0 * i)); + raw_ys.push_back(build_random_array(300.0 * i)); + + auto const& xs = raw_xs.back(); + auto const& ys = raw_ys.back(); + + grid_ids.push_back(inserter(make_span(xs), make_span(ys))); + } + + ASSERT_EQ(num_grids, grids_.size()); + ASSERT_EQ(num_grids, raw_xs.size()); + ASSERT_EQ(num_grids, raw_ys.size()); + ASSERT_EQ(2 * count * num_grids, scalars_.size()); + + for (size_t i = 0; i < num_grids; i++) + { + check_grid(grid_ids[i], raw_xs[i], raw_ys[i]); + } +} + +TEST_F(GenericGridInserterTest, many_with_repeats) +{ + constexpr size_t count = 75; + auto inserter = make_inserter(); + + std::vector grid_ids; + std::array xs = build_random_array(-100.0); + std::vector> raw_ys; + + size_t const num_grids = 20; + for (size_t i = 0; i < num_grids; i++) + { + raw_ys.push_back(build_random_array(300.0 * i)); + + auto const& ys = raw_ys.back(); + + grid_ids.push_back(inserter(make_span(xs), make_span(ys))); + } + + ASSERT_EQ(num_grids, grids_.size()); + ASSERT_EQ(num_grids, raw_ys.size()); + ASSERT_EQ(count * (num_grids + 1), scalars_.size()); + + for (size_t i = 0; i < num_grids; i++) + { + check_grid(grid_ids[i], xs, raw_ys[i]); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas From 9e0674185f971e1e14874966e7add058c559545f Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 15 May 2024 08:38:04 -0400 Subject: [PATCH 47/59] Split ORANGE test into subtests (#1234) --- scripts/dev/iwyu-apple-clang.imp | 2 +- scripts/dev/run-iwyu.sh | 1 - test/Test.hh | 2 +- test/celeritas_test.hh | 2 +- test/orange/CMakeLists.txt | 17 +- test/orange/Orange.test.cc | 1156 +---------------- test/orange/OrangeGeant.test.cc | 232 ++++ test/orange/OrangeGeoTestBase.hh | 1 + test/orange/OrangeJson.test.cc | 831 ++++++++++++ test/orange/OrangeShift.test.cc | 142 ++ .../{Orange.test.cu => OrangeShift.test.cu} | 2 +- test/orange/OrangeTestBase.hh | 1 + test/orange/OrangeTypes.test.cc | 11 +- test/orange/RaytraceImager.test.cc | 2 - 14 files changed, 1241 insertions(+), 1161 deletions(-) create mode 100644 test/orange/OrangeGeant.test.cc create mode 100644 test/orange/OrangeJson.test.cc create mode 100644 test/orange/OrangeShift.test.cc rename test/orange/{Orange.test.cu => OrangeShift.test.cu} (94%) diff --git a/scripts/dev/iwyu-apple-clang.imp b/scripts/dev/iwyu-apple-clang.imp index cdb34092e7..655eddcf53 100644 --- a/scripts/dev/iwyu-apple-clang.imp +++ b/scripts/dev/iwyu-apple-clang.imp @@ -8,7 +8,7 @@ { include: ["@<__algorithm/.*>", private, "", public ] }, { include: ["@<__chrono/.*>", private, "", public ] }, { include: ["@<__functional/.*>", private, "", public ] }, - { include: ["@<__fwd/string_view.h>", private, "", public ] }, + { include: ["@<__fwd/(.*)\\.h>", private, "<\\1>", public ] }, { include: ["@<__iterator/.*>", private, "", public ] }, { include: ["@<__memory/.*>", private, "", public ] }, { include: ["@<__mutex_base>", private, "", public ] }, diff --git a/scripts/dev/run-iwyu.sh b/scripts/dev/run-iwyu.sh index 8227cb3542..40ce06d985 100755 --- a/scripts/dev/run-iwyu.sh +++ b/scripts/dev/run-iwyu.sh @@ -43,5 +43,4 @@ if [ -z "${SKIP_FORMAT}" ]; then git add -u :/ SKIP_GCF=1 git commit --amend -m "IWYU+Clean" >/dev/null git reset HEAD^ - git co HEAD -- ":/src/celeritas/*/generated/*" fi diff --git a/test/Test.hh b/test/Test.hh index 8da88f5469..770bf0f4fd 100644 --- a/test/Test.hh +++ b/test/Test.hh @@ -10,7 +10,7 @@ #include #include #include -#include +#include // IWYU pragma: export #include "celeritas_config.h" diff --git a/test/celeritas_test.hh b/test/celeritas_test.hh index 787602280b..14ace3d6ac 100644 --- a/test/celeritas_test.hh +++ b/test/celeritas_test.hh @@ -11,9 +11,9 @@ #include #include +// IWYU pragma: begin_exports #include "celeritas_config.h" -// IWYU pragma: begin_exports #include "Test.hh" #include "TestMacros.hh" #include "TestMain.hh" diff --git a/test/orange/CMakeLists.txt b/test/orange/CMakeLists.txt index c018e838d4..3a3d911c72 100644 --- a/test/orange/CMakeLists.txt +++ b/test/orange/CMakeLists.txt @@ -30,6 +30,14 @@ if(CELERITAS_USE_Geant4) celeritas_get_g4libs(_g4_geo_libs geometry) endif() +if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") + set(_use_g4org TRUE) + set(_needs_g4org) +else() + set(_use_g4org FALSE) + set(_needs_g4org DISABLE) +endif() + #-----------------------------------------------------------------------------# # SETUP #-----------------------------------------------------------------------------# @@ -45,9 +53,14 @@ celeritas_setup_tests(SERIAL PREFIX orange celeritas_add_test(BoundingBoxUtils.test.cc) celeritas_add_test(MatrixUtils.test.cc) celeritas_add_test(OrangeTypes.test.cc) -celeritas_add_device_test(Orange) celeritas_add_test(RaytraceImager.test.cc GPU) +# High level +celeritas_add_test(Orange.test.cc) +celeritas_add_test(OrangeGeant.test.cc ${_needs_g4org}) +celeritas_add_test(OrangeJson.test.cc ${_needs_json}) +celeritas_add_device_test(OrangeShift ${_needs_json}) + celeritas_add_test(detail/UniverseIndexer.test.cc) # Bounding interval hierarchy @@ -128,7 +141,7 @@ celeritas_add_test(univ/detail/SenseCalculator.test.cc) #-----------------------------------------------------------------------------# # Geant4 construction -if(CELERITAS_USE_Geant4 AND CELERITAS_REAL_TYPE STREQUAL "double") +if(_use_g4org) set(CELERITASTEST_PREFIX orange/g4org) celeritas_add_test(g4org/Converter.test.cc) diff --git a/test/orange/Orange.test.cc b/test/orange/Orange.test.cc index e19178fedc..76e6976830 100644 --- a/test/orange/Orange.test.cc +++ b/test/orange/Orange.test.cc @@ -5,18 +5,15 @@ //---------------------------------------------------------------------------// //! \file orange/Orange.test.cc //---------------------------------------------------------------------------// -#include -#include +#include -#include "corecel/math/Algorithms.hh" -#include "geocel/Types.hh" -#include "geocel/detail/LengthUnits.hh" -#include "orange/OrangeInput.hh" +#include "celeritas_config.h" +#include "corecel/Constants.hh" +#include "corecel/io/Label.hh" #include "orange/OrangeParams.hh" -#include "orange/OrangeParamsOutput.hh" #include "orange/OrangeTrackView.hh" #include "orange/OrangeTypes.hh" -#include "celeritas/Constants.hh" +#include "celeritas/Types.hh" #include "OrangeGeoTestBase.hh" #include "TestMacros.hh" @@ -33,97 +30,9 @@ namespace test class OrangeTest : public OrangeGeoTestBase { protected: - using Initializer_t = GeoTrackInitializer; - - size_type num_track_slots() const override { return 2; } real_type unit_length() const override { return 1; } }; -class JsonOrangeTest : public OrangeTest -{ - public: - void SetUp() final - { - if (!CELERITAS_USE_JSON) - { - GTEST_SKIP() << "JSON is disabled"; - } - this->build_geometry(this->geometry_basename() + ".org.json"); - } -}; - -class GeantOrangeTest : public OrangeTest -{ - public: - void SetUp() final - { - if (!CELERITAS_USE_GEANT4) - { - GTEST_SKIP() << "Geant4 is disabled"; - } - if (CELERITAS_REAL_TYPE != CELERITAS_REAL_TYPE_DOUBLE) - { - GTEST_SKIP() << "Converting Geant4 requires double-precision " - "reals"; - } - this->build_gdml_geometry(this->geometry_basename() + ".gdml"); - } - real_type unit_length() const override { return lengthunits::centimeter; } -}; - -class ShiftTrackerTest : public JsonOrangeTest -{ - protected: - enum class BoundaryState - { - INSIDE = 0, - OUTSIDE = 1 - }; - - std::string geometry_basename() const final { return "hex-array"; } - - CELER_FUNCTION static constexpr unsigned int invalid_id() - { - return static_cast(-1); - } - - void initialize(Real3 pos, Real3 dir) - { - auto track = this->make_geo_track_view(); - track = {pos, dir}; - } - - void distance_to_boundary(real_type& distance) - { - auto track = this->make_geo_track_view(); - distance = track.find_next_step().distance; - } - - void move_to_point(real_type distance) - { - auto track = this->make_geo_track_view(); - track.move_internal(distance); - } - - void move_across_surface(BoundaryState& boundary_state, unsigned int& cell) - { - auto track = this->make_geo_track_view(); - track.move_to_boundary(); - track.cross_boundary(); - - if (!track.is_outside()) - { - boundary_state = BoundaryState::INSIDE; - cell = track.volume_id().get(); - } - else - { - boundary_state = BoundaryState::OUTSIDE; - cell = invalid_id(); - } - } -}; - //---------------------------------------------------------------------------// class OneVolumeTest : public OrangeTest { @@ -574,1061 +483,6 @@ TEST_F(TwoVolumeTest, intersect_limited) EXPECT_FALSE(next.boundary); } -//---------------------------------------------------------------------------// -class FiveVolumesTest : public JsonOrangeTest -{ - std::string geometry_basename() const final { return "five-volumes"; } -}; - -TEST_F(FiveVolumesTest, params) -{ - OrangeParams const& geo = this->params(); - - EXPECT_EQ(6, geo.num_volumes()); - EXPECT_EQ(12, geo.num_surfaces()); - EXPECT_FALSE(geo.supports_safety()); -} - -//---------------------------------------------------------------------------// -class UniversesTest : public JsonOrangeTest -{ - std::string geometry_basename() const final { return "universes"; } -}; - -TEST_F(UniversesTest, params) -{ - OrangeParams const& geo = this->params(); - EXPECT_EQ(12, geo.num_volumes()); - EXPECT_EQ(25, geo.num_surfaces()); - EXPECT_EQ(3, geo.max_depth()); - EXPECT_FALSE(geo.supports_safety()); - - EXPECT_VEC_SOFT_EQ(Real3({-2, -6, -1}), geo.bbox().lower()); - EXPECT_VEC_SOFT_EQ(Real3({8, 4, 2}), geo.bbox().upper()); - - std::vector expected = {"[EXTERIOR]", - "inner_a", - "inner_b", - "bobby", - "johnny", - "[EXTERIOR]", - "inner_c", - "a", - "b", - "c", - "[EXTERIOR]", - "patty"}; - std::vector actual; - for (auto const id : range(VolumeId{geo.num_volumes()})) - { - actual.push_back(geo.id_to_label(id).name); - } - - EXPECT_VEC_EQ(expected, actual); -} - -TEST_F(UniversesTest, tracking) -{ - { - SCOPED_TRACE("patty"); - auto result = this->track({-1.0, -3.75, 0.75}, {1, 0, 0}); - static char const* const expected_volumes[] - = {"johnny", "patty", "c", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 0.5, 5.5, 2}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {0.25, 0.25, 0.25, 0.25}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("inner +x"); - auto result = this->track({-1, -2, 1.0}, {1, 0, 0}); - static char const* const expected_volumes[] - = {"johnny", "c", "a", "b", "c", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 1, 2, 2, 1, 2}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("inner +y"); - auto result = this->track({4, -5, 1.0}, {0, 1, 0}); - static char const* const expected_volumes[] - = {"johnny", "c", "b", "c", "bobby", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 1, 2, 1, 2, 2}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("inner +z"); - auto result = this->track({4, -2, -0.75}, {0, 0, 1}); - static char const* const expected_volumes[] - = {"johnny", "b", "b", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {0.25, 1, 1, 0.5}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {0.125, 0.5, 0.5, 0.25}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } -} - -TEST_F(UniversesTest, TEST_IF_CELERITAS_DOUBLE(output)) -{ - OrangeParamsOutput out(this->geometry()); - EXPECT_EQ("orange", out.label()); - - if (CELERITAS_USE_JSON) - { - EXPECT_JSON_EQ( - R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":14,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":12,"inner_nodes":6,"leaf_nodes":9,"local_volume_ids":12},"connectivity_records":25,"daughters":3,"local_surface_ids":55,"local_volume_ids":21,"logic_ints":171,"real_ids":25,"reals":24,"rect_arrays":0,"simple_units":3,"surface_types":25,"transforms":3,"universe_indices":3,"universe_types":3,"volume_records":12}})json", - to_string(out)); - } -} - -TEST_F(UniversesTest, initialize_with_multiple_universes) -{ - auto geo = this->make_geo_track_view(); - - // Initialize in outermost universe - geo = Initializer_t{{-1, -2, 1}, {1, 0, 0}}; - EXPECT_VEC_SOFT_EQ(Real3({-1, -2, 1}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); - EXPECT_EQ("johnny", this->volume_name(geo)); - EXPECT_FALSE(geo.is_outside()); - EXPECT_FALSE(geo.is_on_boundary()); - - // Initialize in daughter universe - geo = Initializer_t{{0.625, -2, 1}, {1, 0, 0}}; - EXPECT_VEC_SOFT_EQ(Real3({0.625, -2, 1}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); - EXPECT_EQ("c", this->volume_name(geo)); - EXPECT_FALSE(geo.is_outside()); - EXPECT_FALSE(geo.is_on_boundary()); - - // Initialize in daughter universe using "this == &other" - geo = OrangeTrackView::DetailedInitializer{geo, {0, 1, 0}}; - EXPECT_VEC_SOFT_EQ(Real3({0.625, -2, 1}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({0, 1, 0}), geo.dir()); - EXPECT_EQ("c", this->volume_name(geo)); - EXPECT_FALSE(geo.is_outside()); - EXPECT_FALSE(geo.is_on_boundary()); - - { - // Initialize a separate track slot - auto other = this->make_geo_track_view(TrackSlotId{1}); - other = OrangeTrackView::DetailedInitializer{geo, {1, 0, 0}}; - EXPECT_VEC_SOFT_EQ(Real3({0.625, -2, 1}), other.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), other.dir()); - EXPECT_EQ("c", this->params().id_to_label(other.volume_id()).name); - EXPECT_FALSE(other.is_outside()); - EXPECT_FALSE(other.is_on_boundary()); - } -} - -TEST_F(UniversesTest, move_internal_multiple_universes) -{ - auto geo = this->make_geo_track_view(); - - // Initialize in daughter universe - geo = Initializer_t{{0.625, -2, 1}, {0, 1, 0}}; - - // Move internally, then check that the distance to boundary is correct - geo.move_internal({0.625, -1, 1}); - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); - - // Move again, using other move_internal method - geo.move_internal(0.1); - next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.9, next.distance); -} - -// Set direction in a daughter universe and then make sure the direction is -// correctly returned at the top level -TEST_F(UniversesTest, change_dir_daughter_universe) -{ - auto geo = this->make_geo_track_view(); - - // Initialize inside daughter universe a - geo = Initializer_t{{1.5, -2.0, 1.0}, {1.0, 0.0, 0.0}}; - - // Change the direction - geo.set_dir({0.0, 1.0, 0.0}); - - // Get the direction - EXPECT_VEC_EQ(Real3({0.0, 1.0, 0.0}), geo.dir()); -} - -// Cross into daughter universe for the case where the hole cell does not share -// a boundary with another with a parent cell -TEST_F(UniversesTest, cross_into_daughter_non_coincident) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{2, -5, 0.75}, {0, 1, 0}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("johnny", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("c", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("alpha.my", this->surface_name(geo)); -} - -// Cross into parent universe for the case where the hole cell does not share a -// boundary with another with a parent cell -TEST_F(UniversesTest, cross_into_parent_non_coincident) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{2, -3.25, 0.75}, {0, -1, 0}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.75, next.distance); - geo.move_to_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("c", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("johnny", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(2, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("john.my", this->surface_name(geo)); -} - -// Cross into daughter universe for the case where the hole cell shares a -// boundary with another with a parent cell -TEST_F(UniversesTest, cross_into_daughter_coincident) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{2, 1, 1}, {0, -1, 0}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("bob.my", this->surface_name(geo)); - EXPECT_EQ("bobby", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("bob.my", this->surface_name(geo)); - EXPECT_EQ("c", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("alpha.py", this->surface_name(geo)); -} - -// Cross into parent universe for the case where the hole cell shares a -// boundary with another with a parent cell -TEST_F(UniversesTest, cross_into_parent_coincident) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{2, -0.5, 1}, {0, 1, 0}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.5, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("bob.my", this->surface_name(geo)); - EXPECT_EQ("c", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("bob.my", this->surface_name(geo)); - EXPECT_EQ("bobby", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(2, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("bob.py", this->surface_name(geo)); -} - -// Cross into daughter universe that is two levels down -TEST_F(UniversesTest, cross_into_daughter_doubly_coincident) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{0.25, -4.5, 1}, {0, 1, 0}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.5, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("johnny", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("patty", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.5, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("inner_c.py", this->surface_name(geo)); -} - -// Cross into parent universe that is two levels down -TEST_F(UniversesTest, cross_into_parent_doubly_coincident) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{0.25, -3.75, 1}, {0, -1, 0}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.25, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("patty", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("johnny", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(2, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("john.my", this->surface_name(geo)); -} - -// Cross between two daughter universes that share a boundary -TEST_F(UniversesTest, cross_between_daughters) -{ - auto geo = this->make_geo_track_view(); - - // Initialize in outermost universe - geo = Initializer_t{{2, -2, 0.7}, {0, 0, -1}}; - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.2, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("inner_a.pz", this->surface_name(geo)); - EXPECT_EQ("a", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, -2, 0.5}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("inner_a.pz", this->surface_name(geo)); - EXPECT_EQ("a", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, -2, 0.5}), geo.pos()); - - // Make sure we can take another step after crossing - next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("bob.mz", this->surface_name(geo)); -} - -// Change direction on a universe boundary to reenter the cell -TEST_F(UniversesTest, reentrant) -{ - auto geo = this->make_geo_track_view(); - - // Initialize in innermost universe - geo = Initializer_t{{0.25, -3.7, 0.7}, {0, 1, 0}}; - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.2, next.distance); - - // Move to universe boundary - geo.move_to_boundary(); - EXPECT_EQ("inner_c.py", this->surface_name(geo)); - EXPECT_EQ("patty", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -3.5, 0.7}), geo.pos()); - - // Change direction on the universe boundary such that we are no longer - // exiting the universe - geo.set_dir({0, -1, 0}); - - // Remain in same cell after crossing boundary - geo.cross_boundary(); - EXPECT_EQ("inner_c.py", this->surface_name(geo)); - EXPECT_EQ("patty", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -3.5, 0.7}), geo.pos()); - - // Make sure we can take another step after calling cross_boundary - next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.5, next.distance); - geo.move_to_boundary(); - EXPECT_EQ("inner_a.my", this->surface_name(geo)); - EXPECT_EQ("patty", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 0.7}), geo.pos()); -} - -//---------------------------------------------------------------------------// -class RectArrayTest : public JsonOrangeTest -{ - std::string geometry_basename() const final { return "rect-array"; } -}; - -TEST_F(RectArrayTest, params) -{ - OrangeParams const& geo = this->params(); - EXPECT_EQ(35, geo.num_volumes()); - EXPECT_EQ(22, geo.num_surfaces()); - EXPECT_EQ(4, geo.max_depth()); - EXPECT_FALSE(geo.supports_safety()); - - EXPECT_VEC_SOFT_EQ(Real3({-12, -4, -5}), geo.bbox().lower()); - EXPECT_VEC_SOFT_EQ(Real3({12, 10, 5}), geo.bbox().upper()); -} - -TEST_F(RectArrayTest, tracking) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{-1, 1, -1}, {1, 0, 0}}; - - EXPECT_VEC_SOFT_EQ(Real3({-1, 1, -1}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); - EXPECT_EQ("Hfill", this->volume_name(geo)); -} - -//---------------------------------------------------------------------------// - -class NestedRectArraysTest : public JsonOrangeTest -{ - std::string geometry_basename() const final - { - return "nested-rect-arrays"; - } -}; - -TEST_F(NestedRectArraysTest, tracking) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{1.5, 0.5, 0.5}, {1, 0, 0}}; - - EXPECT_VEC_SOFT_EQ(Real3({1.5, 0.5, 0.5}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); - EXPECT_EQ("Afill", this->volume_name(geo)); - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.5, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("{x,1}", this->surface_name(geo)); - EXPECT_EQ("Afill", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, 0.5, 0.5}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("{x,1}", this->surface_name(geo)); - EXPECT_EQ("Bfill", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({2, 0.5, 0.5}), geo.pos()); - - next = geo.find_next_step(); - EXPECT_SOFT_EQ(1, next.distance); -} - -TEST_F(NestedRectArraysTest, leaving) -{ - auto geo = this->make_geo_track_view(); - geo = Initializer_t{{3.5, 1.5, 0.5}, {1, 0, 0}}; - - EXPECT_VEC_SOFT_EQ(Real3({3.5, 1.5, 0.5}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); - EXPECT_EQ("Bfill", this->volume_name(geo)); - - auto next = geo.find_next_step(); - EXPECT_SOFT_EQ(0.5, next.distance); - - geo.move_to_boundary(); - EXPECT_EQ("arrfill.px", this->surface_name(geo)); - EXPECT_EQ("Bfill", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({4, 1.5, 0.5}), geo.pos()); - - // Cross universe boundary - geo.cross_boundary(); - EXPECT_EQ("arrfill.px", this->surface_name(geo)); - EXPECT_EQ("interior", this->volume_name(geo)); - EXPECT_VEC_SOFT_EQ(Real3({4, 1.5, 0.5}), geo.pos()); - - next = geo.find_next_step(); - EXPECT_SOFT_EQ(16, next.distance); -} - -//---------------------------------------------------------------------------// -class Geant4Testem15Test : public JsonOrangeTest -{ - std::string geometry_basename() const final { return "geant4-testem15"; } -}; - -TEST_F(Geant4Testem15Test, safety) -{ - OrangeTrackView geo = this->make_geo_track_view(); - - geo = Initializer_t{{0, 0, 0}, {1, 0, 0}}; - EXPECT_VEC_SOFT_EQ(Real3({0, 0, 0}), geo.pos()); - EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); - EXPECT_EQ(VolumeId{1}, geo.volume_id()); - EXPECT_EQ(SurfaceId{}, geo.surface_id()); - EXPECT_FALSE(geo.is_outside()); - - // Safety at middle should be to the box boundary - EXPECT_SOFT_EQ(5000.0, geo.find_safety()); - - // Check safety near face - auto next = geo.find_next_step(4995.0); - EXPECT_SOFT_EQ(4995.0, next.distance); - EXPECT_FALSE(next.boundary); - geo.move_internal(4995.0); - EXPECT_SOFT_EQ(5.0, geo.find_safety()); - - // Check safety near edge - geo.set_dir({0, 1, 0}); - next = geo.find_next_step(); - geo.move_internal(4990.0); - EXPECT_SOFT_EQ(5.0, geo.find_safety()); - geo.set_dir({-1, 0, 0}); - next = geo.find_next_step(); - geo.move_internal(6.0); - EXPECT_SOFT_EQ(10.0, geo.find_safety()); -} - -//---------------------------------------------------------------------------// - -class HexArrayTest : public JsonOrangeTest -{ - std::string geometry_basename() const final { return "hex-array"; } -}; - -TEST_F(HexArrayTest, TEST_IF_CELERITAS_DOUBLE(output)) -{ - OrangeParamsOutput out(this->geometry()); - EXPECT_EQ("orange", out.label()); - - if (CELERITAS_USE_JSON) - { - EXPECT_JSON_EQ( - R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":9,"max_intersections":10,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":58,"inner_nodes":49,"leaf_nodes":53,"local_volume_ids":58},"connectivity_records":53,"daughters":51,"local_surface_ids":191,"local_volume_ids":348,"logic_ints":585,"real_ids":53,"reals":272,"rect_arrays":0,"simple_units":4,"surface_types":53,"transforms":51,"universe_indices":4,"universe_types":4,"volume_records":58}})json", - to_string(out)); - } -} - -TEST_F(HexArrayTest, track_out) -{ - auto result = this->track( - {-6.9258369494022292, -4.9982766629573767, -10.8378536157757495}, - {0.6750034206933703, -0.3679917428721818, 0.6394939086732125}); - - static char const* const expected_volumes[] - = {"interior", "cfill", "dfill", "interior"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = { - 1.9914318088046, 5.3060674310398, 0.30636846908014, 5.9880767678838}; - EXPECT_VEC_NEAR( - expected_distances, result.distances, 10 * SoftEqual<>{}.rel()); - static real_type const expected_hw_safety[] = { - 0.20109936014143, 0.29549138370648, 0.030952132652541, 0.90113367054536}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); -} - -//---------------------------------------------------------------------------// - -class TestEM3Test : public JsonOrangeTest -{ - std::string geometry_basename() const final { return "testem3"; } -}; - -// Test safety distance within a geometry that supports simple safety -TEST_F(TestEM3Test, safety) -{ - EXPECT_FALSE(this->params().supports_safety()); - - auto geo = this->make_geo_track_view(); - - // Initialize in innermost universe, near the universe boundary - geo = Initializer_t{{19.99, 19.9, 19.9}, {0, 1, 0}}; - EXPECT_SOFT_EQ(0.01, geo.find_safety()); - - // Initialize on the other side of the same volume - geo = Initializer_t{{19.42, 19.9, 19.9}, {0, 1, 0}}; - EXPECT_SOFT_EQ(0.01, geo.find_safety()); -} - -//---------------------------------------------------------------------------// -TEST_F(ShiftTrackerTest, host) -{ - std::vector pos{ - {-0.5466, 1.1298, -1.8526}, - {1.5968, -4.3272, -3.0764}, - {-1.2053, -2.7582, -0.1715}, - {-2.3368, -1.7800, 1.2726}, - {4.0610, 1.5512, 2.8693}, - {-1.5469, 1.6592, -0.6909}, - {-3.6040, -0.7626, -1.7840}, - {4.3726, -2.5543, -0.0444}, - {1.7047, 1.6042, 4.4779}, - {-0.8630, -4.8264, 3.1796}, - }; - std::vector> mu_phi{ - {0.215991, 1.114365}, - {-0.887921, 1.414178}, - {0.727041, 5.874378}, - {0.822052, 3.051407}, - {0.576156, 3.585084}, - {-0.243608, 0.901952}, - {0.486739, 2.328782}, - {0.966572, 4.876337}, - {-0.798997, 0.149136}, - {0.748980, 1.677583}, - }; - - std::vector steps(10, 0); - - for (auto n : range(pos.size())) - { - auto costheta = mu_phi[n][0]; - auto sintheta = std::sqrt(1 - costheta * costheta); - auto phi = mu_phi[n][1]; - Real3 dir - = {sintheta * std::cos(phi), sintheta * std::sin(phi), costheta}; - - this->initialize(pos[n], dir); - - auto dbnd = std::numeric_limits::max(); - auto cell = this->invalid_id(); - BoundaryState bnd_state = BoundaryState::INSIDE; - - while (bnd_state == BoundaryState::INSIDE) - { - this->distance_to_boundary(dbnd); - this->move_across_surface(bnd_state, cell); - - ++steps[n]; - } - } - - std::vector ref_steps = {5, 3, 5, 5, 6, 5, 4, 4, 5, 3}; - EXPECT_VEC_EQ(ref_steps, steps); -} - -//---------------------------------------------------------------------------// - -class InputBuilderTest : public JsonOrangeTest -{ - std::string geometry_basename() const final - { - return const_cast(this)->make_unique_filename(); - } -}; - -TEST_F(InputBuilderTest, globalspheres) -{ - { - auto result = this->track({0, 0, 0}, {0, 0, 1}); - - static char const* const expected_volumes[] = {"inner", "shell"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {5, 5}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {2.5, 2.5}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - - if (CELERITAS_USE_JSON) - { - OrangeParamsOutput out(this->geometry()); - EXPECT_JSON_EQ( - R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":1,"max_faces":2,"max_intersections":4,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":3,"inner_nodes":0,"leaf_nodes":1,"local_volume_ids":3},"connectivity_records":2,"daughters":0,"local_surface_ids":4,"local_volume_ids":4,"logic_ints":7,"real_ids":2,"reals":2,"rect_arrays":0,"simple_units":1,"surface_types":2,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":3}})json", - to_string(out)); - } -} - -//---------------------------------------------------------------------------// -TEST_F(InputBuilderTest, bgspheres) -{ - { - SCOPED_TRACE("from background"); - auto result = this->track({0, 0, -9}, {0, 0, 1}); - - static char const* const expected_volumes[] - = {"global", "bottom", "global", "top", "global"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {3, 6, 1, 4, 5}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - } - { - SCOPED_TRACE("from inside top"); - auto result = this->track({0, 0, 3}, {0, 0, -1}); - - static char const* const expected_volumes[] - = {"top", "global", "bottom", "global"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {2, 1, 6, 4}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - } - - if (CELERITAS_USE_JSON) - { - OrangeParamsOutput out(this->geometry()); - EXPECT_JSON_EQ( - R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":1,"max_faces":3,"max_intersections":6,"max_logic_depth":1,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":4,"inner_nodes":1,"leaf_nodes":2,"local_volume_ids":4},"connectivity_records":3,"daughters":0,"local_surface_ids":6,"local_volume_ids":3,"logic_ints":5,"real_ids":3,"reals":9,"rect_arrays":0,"simple_units":1,"surface_types":3,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":4}})json", - to_string(out)); - } -} - -//---------------------------------------------------------------------------// -TEST_F(InputBuilderTest, universes) -{ - // NOTE: results should be identical to UniversesTest.tracking - { - SCOPED_TRACE("patty"); - auto result = this->track({-1.0, -3.75, 0.75}, {1, 0, 0}); - static char const* const expected_volumes[] - = {"johnny", "patty", "c", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 0.5, 5.5, 2}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {0.25, 0.25, 0.25, 0.25}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("inner +x"); - auto result = this->track({-1, -2, 1.0}, {1, 0, 0}); - static char const* const expected_volumes[] - = {"johnny", "c", "a", "b", "c", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 1, 2, 2, 1, 2}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("inner +y"); - auto result = this->track({4, -5, 1.0}, {0, 1, 0}); - static char const* const expected_volumes[] - = {"johnny", "c", "b", "c", "bobby", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 1, 2, 1, 2, 2}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("inner +z"); - auto result = this->track({4, -2, -0.75}, {0, 0, 1}); - static char const* const expected_volumes[] - = {"johnny", "b", "b", "johnny"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {0.25, 1, 1, 0.5}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {0.125, 0.5, 0.5, 0.25}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } -} - -//---------------------------------------------------------------------------// -TEST_F(InputBuilderTest, hierarchy) -{ - { - SCOPED_TRACE("py"); - auto result = this->track({0, -20, 0}, {0, 1, 0}); - static char const* const expected_volumes[] - = {"interior", "d2", "interior", "d1", "interior"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {14, 2, 8, 2, 94}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - } - { - SCOPED_TRACE("py_filled"); - auto result = this->track({0, -9, -20}, {0, 1, 0}); - static char const* const expected_volumes[] = {"filled_daughter", - "d2", - "filled_daughter", - "d1", - "filled_daughter", - "interior"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] - = {3, 2, 8, 2, 4, 87.979589711327}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {1.5, 5, 4, 5, 2, 39}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - SCOPED_TRACE("pz"); - auto result = this->track({0, 0, -50}, {0, 0, 1}); - static char const* const expected_volumes[] = {"interior", - "filled_daughter", - "leaf1", - "filled_daughter", - "leaf2", - "filled_daughter", - "interior", - "leaf1", - "interior", - "bottom", - "top", - "interior"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] - = {20, 4, 2, 8, 2, 4, 4, 2, 23, 1, 1, 79}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - } - - if (CELERITAS_USE_JSON) - { - OrangeParamsOutput out(this->geometry()); - EXPECT_JSON_EQ( - R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":8,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":24,"inner_nodes":9,"leaf_nodes":16,"local_volume_ids":24},"connectivity_records":13,"daughters":6,"local_surface_ids":20,"local_volume_ids":18,"logic_ints":31,"real_ids":13,"reals":46,"rect_arrays":0,"simple_units":7,"surface_types":13,"transforms":6,"universe_indices":7,"universe_types":7,"volume_records":24}})json", - to_string(out)); - } -} - -//---------------------------------------------------------------------------// -TEST_F(InputBuilderTest, incomplete_bb) -{ - if (CELERITAS_USE_JSON) - { - OrangeParamsOutput out(this->geometry()); - EXPECT_JSON_EQ( - R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":2,"max_faces":6,"max_intersections":6,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":6,"inner_nodes":1,"leaf_nodes":3,"local_volume_ids":6},"connectivity_records":8,"daughters":1,"local_surface_ids":10,"local_volume_ids":4,"logic_ints":38,"real_ids":8,"reals":26,"rect_arrays":0,"simple_units":2,"surface_types":8,"transforms":1,"universe_indices":2,"universe_types":2,"volume_records":6}})json", - to_string(out)); - } -} - -//---------------------------------------------------------------------------// -class TestEm3GeantTest : public GeantOrangeTest -{ - std::string geometry_basename() const final { return "testem3"; } -}; - -TEST_F(TestEm3GeantTest, trace) -{ - { - auto result = this->track({-20.1}, {1, 0, 0}); - - static char const* const expected_volumes[] - = {"World", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", - "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "World"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = { - 0.1, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 4}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {0.050, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 2}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } -} - -//---------------------------------------------------------------------------// -class TestEm3FlatGeantTest : public GeantOrangeTest -{ - std::string geometry_basename() const final { return "testem3-flat"; } -}; - -TEST_F(TestEm3FlatGeantTest, trace) -{ - { - auto result = this->track({-20.1}, {1, 0, 0}); - - static char const* const expected_volumes[] - = {"world", "gap_0", "absorber_0", "gap_1", - "absorber_1", "gap_2", "absorber_2", "gap_3", - "absorber_3", "gap_4", "absorber_4", "gap_5", - "absorber_5", "gap_6", "absorber_6", "gap_7", - "absorber_7", "gap_8", "absorber_8", "gap_9", - "absorber_9", "gap_10", "absorber_10", "gap_11", - "absorber_11", "gap_12", "absorber_12", "gap_13", - "absorber_13", "gap_14", "absorber_14", "gap_15", - "absorber_15", "gap_16", "absorber_16", "gap_17", - "absorber_17", "gap_18", "absorber_18", "gap_19", - "absorber_19", "gap_20", "absorber_20", "gap_21", - "absorber_21", "gap_22", "absorber_22", "gap_23", - "absorber_23", "gap_24", "absorber_24", "gap_25", - "absorber_25", "gap_26", "absorber_26", "gap_27", - "absorber_27", "gap_28", "absorber_28", "gap_29", - "absorber_29", "gap_30", "absorber_30", "gap_31", - "absorber_31", "gap_32", "absorber_32", "gap_33", - "absorber_33", "gap_34", "absorber_34", "gap_35", - "absorber_35", "gap_36", "absorber_36", "gap_37", - "absorber_37", "gap_38", "absorber_38", "gap_39", - "absorber_39", "gap_40", "absorber_40", "gap_41", - "absorber_41", "gap_42", "absorber_42", "gap_43", - "absorber_43", "gap_44", "absorber_44", "gap_45", - "absorber_45", "gap_46", "absorber_46", "gap_47", - "absorber_47", "gap_48", "absorber_48", "gap_49", - "absorber_49", "world"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = { - 0.1, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, - 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, - 0.23, 0.57, 4}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {0.05, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, - 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, - 0.115, 0.285, 2}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } -} - -//---------------------------------------------------------------------------// -class SimpleCmsGeantTest : public GeantOrangeTest -{ - std::string geometry_basename() const final { return "simple-cms"; } -}; - -TEST_F(SimpleCmsGeantTest, trace) -{ - { - auto result = this->track({-75, 0, 0}, {1, 0, 0}); - static char const* const expected_volumes[] = {"si_tracker", - "vacuum_tube", - "si_tracker", - "em_calorimeter", - "had_calorimeter", - "sc_solenoid", - "fe_muon_chambers", - "world"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] - = {45, 60, 95, 50, 100, 100, 325, 300}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] - = {22.5, 700, 47.5, 25, 50, 50, 162.5, 150}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } - { - auto result = this->track({25, 0, 701}, {0, 0, -1}); - static char const* const expected_volumes[] - = {"world", "vacuum_tube", "world"}; - EXPECT_VEC_EQ(expected_volumes, result.volumes); - static real_type const expected_distances[] = {1, 1400, 1300}; - EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); - static real_type const expected_hw_safety[] = {0.5, 5, 5}; - EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); - } -} - -//---------------------------------------------------------------------------// -class ZnenvGeantTest : public GeantOrangeTest -{ - std::string geometry_basename() const final { return "znenv"; } -}; - -TEST_F(ZnenvGeantTest, trace) -{ - static char const* const expected_mid_volumes[] - = {"World", "ZNENV", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", - "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", - "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", - "ZNST", "ZNST", "ZNST", "ZNENV", "World"}; - static real_type const expected_mid_distances[] - = {6.38, 0.1, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, - 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, - 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.1, 46.38}; - { - auto result = this->track({-10, 0.0001, 0}, {1, 0, 0}); - EXPECT_VEC_EQ(expected_mid_volumes, result.volumes); - EXPECT_VEC_SOFT_EQ(expected_mid_distances, result.distances); - } - { - auto result = this->track({0.0001, -10, 0}, {0, 1, 0}); - EXPECT_VEC_EQ(expected_mid_volumes, result.volumes); - EXPECT_VEC_SOFT_EQ(expected_mid_distances, result.distances); - } -} - //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas diff --git a/test/orange/OrangeGeant.test.cc b/test/orange/OrangeGeant.test.cc new file mode 100644 index 0000000000..9a6475f5da --- /dev/null +++ b/test/orange/OrangeGeant.test.cc @@ -0,0 +1,232 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/OrangeGeant.test.cc +//---------------------------------------------------------------------------// +#include + +#include "celeritas_config.h" +#include "corecel/Types.hh" +#include "geocel/detail/LengthUnits.hh" + +#include "OrangeGeoTestBase.hh" +#include "TestMacros.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class GeantOrangeTest : public OrangeGeoTestBase +{ + protected: + void SetUp() final + { + ASSERT_EQ(CELERITAS_REAL_TYPE, CELERITAS_REAL_TYPE_DOUBLE) + << "Converting Geant4 requires double-precision reals"; + this->build_gdml_geometry(this->geometry_basename() + ".gdml"); + } + real_type unit_length() const final { return lengthunits::centimeter; } +}; + +//---------------------------------------------------------------------------// +class TestEm3GeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "testem3"; } +}; + +TEST_F(TestEm3GeantTest, trace) +{ + { + auto result = this->track({-20.1}, {1, 0, 0}); + + static char const* const expected_volumes[] + = {"World", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", + "G4_lAr", "G4_Pb", "G4_lAr", "G4_Pb", "G4_lAr", "World"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = { + 0.1, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 4}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.050, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 2}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +class TestEm3FlatGeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "testem3-flat"; } +}; + +TEST_F(TestEm3FlatGeantTest, trace) +{ + { + auto result = this->track({-20.1}, {1, 0, 0}); + + static char const* const expected_volumes[] + = {"world", "gap_0", "absorber_0", "gap_1", + "absorber_1", "gap_2", "absorber_2", "gap_3", + "absorber_3", "gap_4", "absorber_4", "gap_5", + "absorber_5", "gap_6", "absorber_6", "gap_7", + "absorber_7", "gap_8", "absorber_8", "gap_9", + "absorber_9", "gap_10", "absorber_10", "gap_11", + "absorber_11", "gap_12", "absorber_12", "gap_13", + "absorber_13", "gap_14", "absorber_14", "gap_15", + "absorber_15", "gap_16", "absorber_16", "gap_17", + "absorber_17", "gap_18", "absorber_18", "gap_19", + "absorber_19", "gap_20", "absorber_20", "gap_21", + "absorber_21", "gap_22", "absorber_22", "gap_23", + "absorber_23", "gap_24", "absorber_24", "gap_25", + "absorber_25", "gap_26", "absorber_26", "gap_27", + "absorber_27", "gap_28", "absorber_28", "gap_29", + "absorber_29", "gap_30", "absorber_30", "gap_31", + "absorber_31", "gap_32", "absorber_32", "gap_33", + "absorber_33", "gap_34", "absorber_34", "gap_35", + "absorber_35", "gap_36", "absorber_36", "gap_37", + "absorber_37", "gap_38", "absorber_38", "gap_39", + "absorber_39", "gap_40", "absorber_40", "gap_41", + "absorber_41", "gap_42", "absorber_42", "gap_43", + "absorber_43", "gap_44", "absorber_44", "gap_45", + "absorber_45", "gap_46", "absorber_46", "gap_47", + "absorber_47", "gap_48", "absorber_48", "gap_49", + "absorber_49", "world"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = { + 0.1, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, + 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, 0.23, 0.57, + 0.23, 0.57, 4}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.05, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, + 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, 0.115, 0.285, + 0.115, 0.285, 2}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +class SimpleCmsGeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "simple-cms"; } +}; + +TEST_F(SimpleCmsGeantTest, trace) +{ + { + auto result = this->track({-75, 0, 0}, {1, 0, 0}); + static char const* const expected_volumes[] = {"si_tracker", + "vacuum_tube", + "si_tracker", + "em_calorimeter", + "had_calorimeter", + "sc_solenoid", + "fe_muon_chambers", + "world"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] + = {45, 60, 95, 50, 100, 100, 325, 300}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {22.5, 700, 47.5, 25, 50, 50, 162.5, 150}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + auto result = this->track({25, 0, 701}, {0, 0, -1}); + static char const* const expected_volumes[] + = {"world", "vacuum_tube", "world"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 1400, 1300}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {0.5, 5, 5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +class ZnenvGeantTest : public GeantOrangeTest +{ + std::string geometry_basename() const final { return "znenv"; } +}; + +TEST_F(ZnenvGeantTest, trace) +{ + static char const* const expected_mid_volumes[] + = {"World", "ZNENV", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", + "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", + "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", "ZNST", + "ZNST", "ZNST", "ZNST", "ZNENV", "World"}; + static real_type const expected_mid_distances[] + = {6.38, 0.1, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, + 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, + 0.32, 0.32, 0.32, 0.32, 0.32, 0.32, 0.1, 46.38}; + { + auto result = this->track({-10, 0.0001, 0}, {1, 0, 0}); + EXPECT_VEC_EQ(expected_mid_volumes, result.volumes); + EXPECT_VEC_SOFT_EQ(expected_mid_distances, result.distances); + } + { + auto result = this->track({0.0001, -10, 0}, {0, 1, 0}); + EXPECT_VEC_EQ(expected_mid_volumes, result.volumes); + EXPECT_VEC_SOFT_EQ(expected_mid_distances, result.distances); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/orange/OrangeGeoTestBase.hh b/test/orange/OrangeGeoTestBase.hh index 170506d67a..7f89ca8642 100644 --- a/test/orange/OrangeGeoTestBase.hh +++ b/test/orange/OrangeGeoTestBase.hh @@ -43,6 +43,7 @@ class OrangeGeoTestBase : public OrangeTestBase using HostParamsRef = HostCRef; using Params = OrangeParams; using SPConstParams = std::shared_ptr; + using Initializer_t = GeoTrackInitializer; //!@} //!@{ diff --git a/test/orange/OrangeJson.test.cc b/test/orange/OrangeJson.test.cc new file mode 100644 index 0000000000..a24b267160 --- /dev/null +++ b/test/orange/OrangeJson.test.cc @@ -0,0 +1,831 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/OrangeJson.test.cc +//---------------------------------------------------------------------------// +#include +#include + +#include "corecel/cont/Range.hh" +#include "corecel/io/Label.hh" +#include "corecel/io/OutputInterface.hh" +#include "corecel/math/SoftEqual.hh" +#include "geocel/Types.hh" +#include "orange/OrangeParams.hh" +#include "orange/OrangeParamsOutput.hh" +#include "orange/OrangeTrackView.hh" +#include "celeritas/Types.hh" + +#include "OrangeGeoTestBase.hh" +#include "TestMacros.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class JsonOrangeTest : public OrangeGeoTestBase +{ + public: + size_type num_track_slots() const override { return 2; } + real_type unit_length() const override { return 1; } + + void SetUp() final + { + this->build_geometry(this->geometry_basename() + ".org.json"); + } +}; + +class InputBuilderTest : public JsonOrangeTest +{ + std::string geometry_basename() const final + { + return const_cast(this)->make_unique_filename(); + } +}; + +//---------------------------------------------------------------------------// +class FiveVolumesTest : public JsonOrangeTest +{ + std::string geometry_basename() const final { return "five-volumes"; } +}; + +TEST_F(FiveVolumesTest, params) +{ + OrangeParams const& geo = this->params(); + + EXPECT_EQ(6, geo.num_volumes()); + EXPECT_EQ(12, geo.num_surfaces()); + EXPECT_FALSE(geo.supports_safety()); +} + +//---------------------------------------------------------------------------// +class UniversesTest : public JsonOrangeTest +{ + std::string geometry_basename() const final { return "universes"; } +}; + +TEST_F(UniversesTest, params) +{ + OrangeParams const& geo = this->params(); + EXPECT_EQ(12, geo.num_volumes()); + EXPECT_EQ(25, geo.num_surfaces()); + EXPECT_EQ(3, geo.max_depth()); + EXPECT_FALSE(geo.supports_safety()); + + EXPECT_VEC_SOFT_EQ(Real3({-2, -6, -1}), geo.bbox().lower()); + EXPECT_VEC_SOFT_EQ(Real3({8, 4, 2}), geo.bbox().upper()); + + std::vector expected = {"[EXTERIOR]", + "inner_a", + "inner_b", + "bobby", + "johnny", + "[EXTERIOR]", + "inner_c", + "a", + "b", + "c", + "[EXTERIOR]", + "patty"}; + std::vector actual; + for (auto const id : range(VolumeId{geo.num_volumes()})) + { + actual.push_back(geo.id_to_label(id).name); + } + + EXPECT_VEC_EQ(expected, actual); +} + +TEST_F(UniversesTest, tracking) +{ + { + SCOPED_TRACE("patty"); + auto result = this->track({-1.0, -3.75, 0.75}, {1, 0, 0}); + static char const* const expected_volumes[] + = {"johnny", "patty", "c", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 0.5, 5.5, 2}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {0.25, 0.25, 0.25, 0.25}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("inner +x"); + auto result = this->track({-1, -2, 1.0}, {1, 0, 0}); + static char const* const expected_volumes[] + = {"johnny", "c", "a", "b", "c", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 1, 2, 2, 1, 2}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("inner +y"); + auto result = this->track({4, -5, 1.0}, {0, 1, 0}); + static char const* const expected_volumes[] + = {"johnny", "c", "b", "c", "bobby", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 1, 2, 1, 2, 2}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("inner +z"); + auto result = this->track({4, -2, -0.75}, {0, 0, 1}); + static char const* const expected_volumes[] + = {"johnny", "b", "b", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {0.25, 1, 1, 0.5}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {0.125, 0.5, 0.5, 0.25}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +TEST_F(UniversesTest, TEST_IF_CELERITAS_DOUBLE(output)) +{ + OrangeParamsOutput out(this->geometry()); + EXPECT_EQ("orange", out.label()); + + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":14,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":12,"inner_nodes":6,"leaf_nodes":9,"local_volume_ids":12},"connectivity_records":25,"daughters":3,"local_surface_ids":55,"local_volume_ids":21,"logic_ints":171,"real_ids":25,"reals":24,"rect_arrays":0,"simple_units":3,"surface_types":25,"transforms":3,"universe_indices":3,"universe_types":3,"volume_records":12}})json", + to_string(out)); +} + +TEST_F(UniversesTest, initialize_with_multiple_universes) +{ + auto geo = this->make_geo_track_view(); + + // Initialize in outermost universe + geo = Initializer_t{{-1, -2, 1}, {1, 0, 0}}; + EXPECT_VEC_SOFT_EQ(Real3({-1, -2, 1}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); + EXPECT_EQ("johnny", this->volume_name(geo)); + EXPECT_FALSE(geo.is_outside()); + EXPECT_FALSE(geo.is_on_boundary()); + + // Initialize in daughter universe + geo = Initializer_t{{0.625, -2, 1}, {1, 0, 0}}; + EXPECT_VEC_SOFT_EQ(Real3({0.625, -2, 1}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); + EXPECT_EQ("c", this->volume_name(geo)); + EXPECT_FALSE(geo.is_outside()); + EXPECT_FALSE(geo.is_on_boundary()); + + // Initialize in daughter universe using "this == &other" + geo = OrangeTrackView::DetailedInitializer{geo, {0, 1, 0}}; + EXPECT_VEC_SOFT_EQ(Real3({0.625, -2, 1}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({0, 1, 0}), geo.dir()); + EXPECT_EQ("c", this->volume_name(geo)); + EXPECT_FALSE(geo.is_outside()); + EXPECT_FALSE(geo.is_on_boundary()); + + { + // Initialize a separate track slot + auto other = this->make_geo_track_view(TrackSlotId{1}); + other = OrangeTrackView::DetailedInitializer{geo, {1, 0, 0}}; + EXPECT_VEC_SOFT_EQ(Real3({0.625, -2, 1}), other.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), other.dir()); + EXPECT_EQ("c", this->params().id_to_label(other.volume_id()).name); + EXPECT_FALSE(other.is_outside()); + EXPECT_FALSE(other.is_on_boundary()); + } +} + +TEST_F(UniversesTest, move_internal_multiple_universes) +{ + auto geo = this->make_geo_track_view(); + + // Initialize in daughter universe + geo = Initializer_t{{0.625, -2, 1}, {0, 1, 0}}; + + // Move internally, then check that the distance to boundary is correct + geo.move_internal({0.625, -1, 1}); + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); + + // Move again, using other move_internal method + geo.move_internal(0.1); + next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.9, next.distance); +} + +// Set direction in a daughter universe and then make sure the direction is +// correctly returned at the top level +TEST_F(UniversesTest, change_dir_daughter_universe) +{ + auto geo = this->make_geo_track_view(); + + // Initialize inside daughter universe a + geo = Initializer_t{{1.5, -2.0, 1.0}, {1.0, 0.0, 0.0}}; + + // Change the direction + geo.set_dir({0.0, 1.0, 0.0}); + + // Get the direction + EXPECT_VEC_EQ(Real3({0.0, 1.0, 0.0}), geo.dir()); +} + +// Cross into daughter universe for the case where the hole cell does not share +// a boundary with another with a parent cell +TEST_F(UniversesTest, cross_into_daughter_non_coincident) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{2, -5, 0.75}, {0, 1, 0}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("johnny", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("c", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("alpha.my", this->surface_name(geo)); +} + +// Cross into parent universe for the case where the hole cell does not share a +// boundary with another with a parent cell +TEST_F(UniversesTest, cross_into_parent_non_coincident) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{2, -3.25, 0.75}, {0, -1, 0}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.75, next.distance); + geo.move_to_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("c", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("johnny", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, -4, 0.75}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(2, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("john.my", this->surface_name(geo)); +} + +// Cross into daughter universe for the case where the hole cell shares a +// boundary with another with a parent cell +TEST_F(UniversesTest, cross_into_daughter_coincident) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{2, 1, 1}, {0, -1, 0}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("bob.my", this->surface_name(geo)); + EXPECT_EQ("bobby", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("bob.my", this->surface_name(geo)); + EXPECT_EQ("c", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("alpha.py", this->surface_name(geo)); +} + +// Cross into parent universe for the case where the hole cell shares a +// boundary with another with a parent cell +TEST_F(UniversesTest, cross_into_parent_coincident) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{2, -0.5, 1}, {0, 1, 0}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.5, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("bob.my", this->surface_name(geo)); + EXPECT_EQ("c", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("bob.my", this->surface_name(geo)); + EXPECT_EQ("bobby", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, 0, 1}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(2, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("bob.py", this->surface_name(geo)); +} + +// Cross into daughter universe that is two levels down +TEST_F(UniversesTest, cross_into_daughter_doubly_coincident) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{0.25, -4.5, 1}, {0, 1, 0}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.5, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("johnny", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("patty", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.5, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("inner_c.py", this->surface_name(geo)); +} + +// Cross into parent universe that is two levels down +TEST_F(UniversesTest, cross_into_parent_doubly_coincident) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{0.25, -3.75, 1}, {0, -1, 0}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.25, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("patty", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("johnny", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 1}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(2, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("john.my", this->surface_name(geo)); +} + +// Cross between two daughter universes that share a boundary +TEST_F(UniversesTest, cross_between_daughters) +{ + auto geo = this->make_geo_track_view(); + + // Initialize in outermost universe + geo = Initializer_t{{2, -2, 0.7}, {0, 0, -1}}; + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.2, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("inner_a.pz", this->surface_name(geo)); + EXPECT_EQ("a", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, -2, 0.5}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("inner_a.pz", this->surface_name(geo)); + EXPECT_EQ("a", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, -2, 0.5}), geo.pos()); + + // Make sure we can take another step after crossing + next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("bob.mz", this->surface_name(geo)); +} + +// Change direction on a universe boundary to reenter the cell +TEST_F(UniversesTest, reentrant) +{ + auto geo = this->make_geo_track_view(); + + // Initialize in innermost universe + geo = Initializer_t{{0.25, -3.7, 0.7}, {0, 1, 0}}; + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.2, next.distance); + + // Move to universe boundary + geo.move_to_boundary(); + EXPECT_EQ("inner_c.py", this->surface_name(geo)); + EXPECT_EQ("patty", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -3.5, 0.7}), geo.pos()); + + // Change direction on the universe boundary such that we are no longer + // exiting the universe + geo.set_dir({0, -1, 0}); + + // Remain in same cell after crossing boundary + geo.cross_boundary(); + EXPECT_EQ("inner_c.py", this->surface_name(geo)); + EXPECT_EQ("patty", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -3.5, 0.7}), geo.pos()); + + // Make sure we can take another step after calling cross_boundary + next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.5, next.distance); + geo.move_to_boundary(); + EXPECT_EQ("inner_a.my", this->surface_name(geo)); + EXPECT_EQ("patty", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({0.25, -4, 0.7}), geo.pos()); +} + +//---------------------------------------------------------------------------// +class RectArrayTest : public JsonOrangeTest +{ + std::string geometry_basename() const final { return "rect-array"; } +}; + +TEST_F(RectArrayTest, params) +{ + OrangeParams const& geo = this->params(); + EXPECT_EQ(35, geo.num_volumes()); + EXPECT_EQ(22, geo.num_surfaces()); + EXPECT_EQ(4, geo.max_depth()); + EXPECT_FALSE(geo.supports_safety()); + + EXPECT_VEC_SOFT_EQ(Real3({-12, -4, -5}), geo.bbox().lower()); + EXPECT_VEC_SOFT_EQ(Real3({12, 10, 5}), geo.bbox().upper()); +} + +TEST_F(RectArrayTest, tracking) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{-1, 1, -1}, {1, 0, 0}}; + + EXPECT_VEC_SOFT_EQ(Real3({-1, 1, -1}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); + EXPECT_EQ("Hfill", this->volume_name(geo)); +} + +//---------------------------------------------------------------------------// + +class NestedRectArraysTest : public JsonOrangeTest +{ + std::string geometry_basename() const final + { + return "nested-rect-arrays"; + } +}; + +TEST_F(NestedRectArraysTest, tracking) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{1.5, 0.5, 0.5}, {1, 0, 0}}; + + EXPECT_VEC_SOFT_EQ(Real3({1.5, 0.5, 0.5}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); + EXPECT_EQ("Afill", this->volume_name(geo)); + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.5, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("{x,1}", this->surface_name(geo)); + EXPECT_EQ("Afill", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, 0.5, 0.5}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("{x,1}", this->surface_name(geo)); + EXPECT_EQ("Bfill", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({2, 0.5, 0.5}), geo.pos()); + + next = geo.find_next_step(); + EXPECT_SOFT_EQ(1, next.distance); +} + +TEST_F(NestedRectArraysTest, leaving) +{ + auto geo = this->make_geo_track_view(); + geo = Initializer_t{{3.5, 1.5, 0.5}, {1, 0, 0}}; + + EXPECT_VEC_SOFT_EQ(Real3({3.5, 1.5, 0.5}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); + EXPECT_EQ("Bfill", this->volume_name(geo)); + + auto next = geo.find_next_step(); + EXPECT_SOFT_EQ(0.5, next.distance); + + geo.move_to_boundary(); + EXPECT_EQ("arrfill.px", this->surface_name(geo)); + EXPECT_EQ("Bfill", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({4, 1.5, 0.5}), geo.pos()); + + // Cross universe boundary + geo.cross_boundary(); + EXPECT_EQ("arrfill.px", this->surface_name(geo)); + EXPECT_EQ("interior", this->volume_name(geo)); + EXPECT_VEC_SOFT_EQ(Real3({4, 1.5, 0.5}), geo.pos()); + + next = geo.find_next_step(); + EXPECT_SOFT_EQ(16, next.distance); +} + +//---------------------------------------------------------------------------// +class Geant4Testem15Test : public JsonOrangeTest +{ + std::string geometry_basename() const final { return "geant4-testem15"; } +}; + +TEST_F(Geant4Testem15Test, safety) +{ + OrangeTrackView geo = this->make_geo_track_view(); + + geo = Initializer_t{{0, 0, 0}, {1, 0, 0}}; + EXPECT_VEC_SOFT_EQ(Real3({0, 0, 0}), geo.pos()); + EXPECT_VEC_SOFT_EQ(Real3({1, 0, 0}), geo.dir()); + EXPECT_EQ(VolumeId{1}, geo.volume_id()); + EXPECT_EQ(SurfaceId{}, geo.surface_id()); + EXPECT_FALSE(geo.is_outside()); + + // Safety at middle should be to the box boundary + EXPECT_SOFT_EQ(5000.0, geo.find_safety()); + + // Check safety near face + auto next = geo.find_next_step(4995.0); + EXPECT_SOFT_EQ(4995.0, next.distance); + EXPECT_FALSE(next.boundary); + geo.move_internal(4995.0); + EXPECT_SOFT_EQ(5.0, geo.find_safety()); + + // Check safety near edge + geo.set_dir({0, 1, 0}); + next = geo.find_next_step(); + geo.move_internal(4990.0); + EXPECT_SOFT_EQ(5.0, geo.find_safety()); + geo.set_dir({-1, 0, 0}); + next = geo.find_next_step(); + geo.move_internal(6.0); + EXPECT_SOFT_EQ(10.0, geo.find_safety()); +} + +//---------------------------------------------------------------------------// + +class HexArrayTest : public JsonOrangeTest +{ + std::string geometry_basename() const final { return "hex-array"; } +}; + +TEST_F(HexArrayTest, TEST_IF_CELERITAS_DOUBLE(output)) +{ + OrangeParamsOutput out(this->geometry()); + EXPECT_EQ("orange", out.label()); + + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":9,"max_intersections":10,"max_logic_depth":3,"tol":{"abs":1.5e-08,"rel":1.5e-08}},"sizes":{"bih":{"bboxes":58,"inner_nodes":49,"leaf_nodes":53,"local_volume_ids":58},"connectivity_records":53,"daughters":51,"local_surface_ids":191,"local_volume_ids":348,"logic_ints":585,"real_ids":53,"reals":272,"rect_arrays":0,"simple_units":4,"surface_types":53,"transforms":51,"universe_indices":4,"universe_types":4,"volume_records":58}})json", + to_string(out)); +} + +TEST_F(HexArrayTest, track_out) +{ + auto result = this->track( + {-6.9258369494022292, -4.9982766629573767, -10.8378536157757495}, + {0.6750034206933703, -0.3679917428721818, 0.6394939086732125}); + + static char const* const expected_volumes[] + = {"interior", "cfill", "dfill", "interior"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = { + 1.9914318088046, 5.3060674310398, 0.30636846908014, 5.9880767678838}; + EXPECT_VEC_NEAR( + expected_distances, result.distances, 10 * SoftEqual<>{}.rel()); + static real_type const expected_hw_safety[] = { + 0.20109936014143, 0.29549138370648, 0.030952132652541, 0.90113367054536}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); +} + +//---------------------------------------------------------------------------// + +class TestEM3Test : public JsonOrangeTest +{ + std::string geometry_basename() const final { return "testem3"; } +}; + +// Test safety distance within a geometry that supports simple safety +TEST_F(TestEM3Test, safety) +{ + EXPECT_FALSE(this->params().supports_safety()); + + auto geo = this->make_geo_track_view(); + + // Initialize in innermost universe, near the universe boundary + geo = Initializer_t{{19.99, 19.9, 19.9}, {0, 1, 0}}; + EXPECT_SOFT_EQ(0.01, geo.find_safety()); + + // Initialize on the other side of the same volume + geo = Initializer_t{{19.42, 19.9, 19.9}, {0, 1, 0}}; + EXPECT_SOFT_EQ(0.01, geo.find_safety()); +} + +//---------------------------------------------------------------------------// + +TEST_F(InputBuilderTest, globalspheres) +{ + { + auto result = this->track({0, 0, 0}, {0, 0, 1}); + + static char const* const expected_volumes[] = {"inner", "shell"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {5, 5}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {2.5, 2.5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + + OrangeParamsOutput out(this->geometry()); + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":1,"max_faces":2,"max_intersections":4,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":3,"inner_nodes":0,"leaf_nodes":1,"local_volume_ids":3},"connectivity_records":2,"daughters":0,"local_surface_ids":4,"local_volume_ids":4,"logic_ints":7,"real_ids":2,"reals":2,"rect_arrays":0,"simple_units":1,"surface_types":2,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":3}})json", + to_string(out)); +} + +//---------------------------------------------------------------------------// +TEST_F(InputBuilderTest, bgspheres) +{ + { + SCOPED_TRACE("from background"); + auto result = this->track({0, 0, -9}, {0, 0, 1}); + + static char const* const expected_volumes[] + = {"global", "bottom", "global", "top", "global"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {3, 6, 1, 4, 5}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + } + { + SCOPED_TRACE("from inside top"); + auto result = this->track({0, 0, 3}, {0, 0, -1}); + + static char const* const expected_volumes[] + = {"top", "global", "bottom", "global"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {2, 1, 6, 4}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + } + + OrangeParamsOutput out(this->geometry()); + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":1,"max_faces":3,"max_intersections":6,"max_logic_depth":1,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":4,"inner_nodes":1,"leaf_nodes":2,"local_volume_ids":4},"connectivity_records":3,"daughters":0,"local_surface_ids":6,"local_volume_ids":3,"logic_ints":5,"real_ids":3,"reals":9,"rect_arrays":0,"simple_units":1,"surface_types":3,"transforms":0,"universe_indices":1,"universe_types":1,"volume_records":4}})json", + to_string(out)); +} + +//---------------------------------------------------------------------------// +TEST_F(InputBuilderTest, universes) +{ + // NOTE: results should be identical to UniversesTest.tracking + { + SCOPED_TRACE("patty"); + auto result = this->track({-1.0, -3.75, 0.75}, {1, 0, 0}); + static char const* const expected_volumes[] + = {"johnny", "patty", "c", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 0.5, 5.5, 2}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {0.25, 0.25, 0.25, 0.25}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("inner +x"); + auto result = this->track({-1, -2, 1.0}, {1, 0, 0}); + static char const* const expected_volumes[] + = {"johnny", "c", "a", "b", "c", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 1, 2, 2, 1, 2}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("inner +y"); + auto result = this->track({4, -5, 1.0}, {0, 1, 0}); + static char const* const expected_volumes[] + = {"johnny", "c", "b", "c", "bobby", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {1, 1, 2, 1, 2, 2}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] + = {0.5, 0, 0.5, 0.5, 0.5, 0.5}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("inner +z"); + auto result = this->track({4, -2, -0.75}, {0, 0, 1}); + static char const* const expected_volumes[] + = {"johnny", "b", "b", "johnny"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {0.25, 1, 1, 0.5}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {0.125, 0.5, 0.5, 0.25}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } +} + +//---------------------------------------------------------------------------// +TEST_F(InputBuilderTest, hierarchy) +{ + { + SCOPED_TRACE("py"); + auto result = this->track({0, -20, 0}, {0, 1, 0}); + static char const* const expected_volumes[] + = {"interior", "d2", "interior", "d1", "interior"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] = {14, 2, 8, 2, 94}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + } + { + SCOPED_TRACE("py_filled"); + auto result = this->track({0, -9, -20}, {0, 1, 0}); + static char const* const expected_volumes[] = {"filled_daughter", + "d2", + "filled_daughter", + "d1", + "filled_daughter", + "interior"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] + = {3, 2, 8, 2, 4, 87.979589711327}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + static real_type const expected_hw_safety[] = {1.5, 5, 4, 5, 2, 39}; + EXPECT_VEC_SOFT_EQ(expected_hw_safety, result.halfway_safeties); + } + { + SCOPED_TRACE("pz"); + auto result = this->track({0, 0, -50}, {0, 0, 1}); + static char const* const expected_volumes[] = {"interior", + "filled_daughter", + "leaf1", + "filled_daughter", + "leaf2", + "filled_daughter", + "interior", + "leaf1", + "interior", + "bottom", + "top", + "interior"}; + EXPECT_VEC_EQ(expected_volumes, result.volumes); + static real_type const expected_distances[] + = {20, 4, 2, 8, 2, 4, 4, 2, 23, 1, 1, 79}; + EXPECT_VEC_SOFT_EQ(expected_distances, result.distances); + } + + OrangeParamsOutput out(this->geometry()); + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":3,"max_faces":8,"max_intersections":14,"max_logic_depth":3,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":24,"inner_nodes":9,"leaf_nodes":16,"local_volume_ids":24},"connectivity_records":13,"daughters":6,"local_surface_ids":20,"local_volume_ids":18,"logic_ints":31,"real_ids":13,"reals":46,"rect_arrays":0,"simple_units":7,"surface_types":13,"transforms":6,"universe_indices":7,"universe_types":7,"volume_records":24}})json", + to_string(out)); +} + +//---------------------------------------------------------------------------// +TEST_F(InputBuilderTest, incomplete_bb) +{ + OrangeParamsOutput out(this->geometry()); + EXPECT_JSON_EQ( + R"json({"_category":"internal","_label":"orange","scalars":{"max_depth":2,"max_faces":6,"max_intersections":6,"max_logic_depth":2,"tol":{"abs":1e-05,"rel":1e-05}},"sizes":{"bih":{"bboxes":6,"inner_nodes":1,"leaf_nodes":3,"local_volume_ids":6},"connectivity_records":8,"daughters":1,"local_surface_ids":10,"local_volume_ids":4,"logic_ints":38,"real_ids":8,"reals":26,"rect_arrays":0,"simple_units":2,"surface_types":8,"transforms":1,"universe_indices":2,"universe_types":2,"volume_records":6}})json", + to_string(out)); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/orange/OrangeShift.test.cc b/test/orange/OrangeShift.test.cc new file mode 100644 index 0000000000..7259390558 --- /dev/null +++ b/test/orange/OrangeShift.test.cc @@ -0,0 +1,142 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/OrangeShift.test.cc +//---------------------------------------------------------------------------// +#include +#include +#include +#include +#include + +#include "corecel/cont/Array.hh" +#include "corecel/cont/Range.hh" +#include "geocel/Types.hh" +#include "orange/OrangeTrackView.hh" +#include "celeritas/Types.hh" + +#include "OrangeGeoTestBase.hh" +#include "TestMacros.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class ShiftTrackerTest : public OrangeGeoTestBase +{ + protected: + enum class BoundaryState + { + INSIDE = 0, + OUTSIDE = 1 + }; + + void SetUp() final { this->build_geometry("hex-array.org.json"); } + + CELER_FUNCTION static constexpr unsigned int invalid_id() + { + return static_cast(-1); + } + + void initialize(Real3 pos, Real3 dir) + { + auto track = this->make_geo_track_view(); + track = {pos, dir}; + } + + void distance_to_boundary(real_type& distance) + { + auto track = this->make_geo_track_view(); + distance = track.find_next_step().distance; + } + + void move_to_point(real_type distance) + { + auto track = this->make_geo_track_view(); + track.move_internal(distance); + } + + void move_across_surface(BoundaryState& boundary_state, unsigned int& cell) + { + auto track = this->make_geo_track_view(); + track.move_to_boundary(); + track.cross_boundary(); + + if (!track.is_outside()) + { + boundary_state = BoundaryState::INSIDE; + cell = track.volume_id().get(); + } + else + { + boundary_state = BoundaryState::OUTSIDE; + cell = invalid_id(); + } + } +}; + +//---------------------------------------------------------------------------// +TEST_F(ShiftTrackerTest, host) +{ + std::vector pos{ + {-0.5466, 1.1298, -1.8526}, + {1.5968, -4.3272, -3.0764}, + {-1.2053, -2.7582, -0.1715}, + {-2.3368, -1.7800, 1.2726}, + {4.0610, 1.5512, 2.8693}, + {-1.5469, 1.6592, -0.6909}, + {-3.6040, -0.7626, -1.7840}, + {4.3726, -2.5543, -0.0444}, + {1.7047, 1.6042, 4.4779}, + {-0.8630, -4.8264, 3.1796}, + }; + std::vector> mu_phi{ + {0.215991, 1.114365}, + {-0.887921, 1.414178}, + {0.727041, 5.874378}, + {0.822052, 3.051407}, + {0.576156, 3.585084}, + {-0.243608, 0.901952}, + {0.486739, 2.328782}, + {0.966572, 4.876337}, + {-0.798997, 0.149136}, + {0.748980, 1.677583}, + }; + + std::vector steps(10, 0); + + for (auto n : range(pos.size())) + { + auto costheta = mu_phi[n][0]; + auto sintheta = std::sqrt(1 - costheta * costheta); + auto phi = mu_phi[n][1]; + Real3 dir + = {sintheta * std::cos(phi), sintheta * std::sin(phi), costheta}; + + this->initialize(pos[n], dir); + + auto dbnd = std::numeric_limits::max(); + auto cell = this->invalid_id(); + BoundaryState bnd_state = BoundaryState::INSIDE; + + while (bnd_state == BoundaryState::INSIDE) + { + this->distance_to_boundary(dbnd); + this->move_across_surface(bnd_state, cell); + + ++steps[n]; + } + } + + std::vector ref_steps = {5, 3, 5, 5, 6, 5, 4, 4, 5, 3}; + EXPECT_VEC_EQ(ref_steps, steps); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/orange/Orange.test.cu b/test/orange/OrangeShift.test.cu similarity index 94% rename from test/orange/Orange.test.cu rename to test/orange/OrangeShift.test.cu index f7ebd5faa9..cd6eaecb4b 100644 --- a/test/orange/Orange.test.cu +++ b/test/orange/OrangeShift.test.cu @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/Orange.test.cu +//! \file orange/OrangeShift.test.cu //---------------------------------------------------------------------------// // Simply include these because SCALE does too :eyeroll: diff --git a/test/orange/OrangeTestBase.hh b/test/orange/OrangeTestBase.hh index 9d4d256ecf..7f77bd6ff9 100644 --- a/test/orange/OrangeTestBase.hh +++ b/test/orange/OrangeTestBase.hh @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "corecel/OpaqueId.hh" #include "geocel/GenericGeoTestBase.hh" #include "orange/OrangeData.hh" #include "orange/OrangeGeoTraits.hh" diff --git a/test/orange/OrangeTypes.test.cc b/test/orange/OrangeTypes.test.cc index 73546d7835..776fe4c683 100644 --- a/test/orange/OrangeTypes.test.cc +++ b/test/orange/OrangeTypes.test.cc @@ -7,8 +7,17 @@ //---------------------------------------------------------------------------// #include "orange/OrangeTypes.hh" +#include +#include +#include +#include +#include +#include + +#include "corecel/Types.hh" +#include "corecel/math/Algorithms.hh" + #include "celeritas_test.hh" -// #include "OrangeTypes.test.hh" namespace celeritas { diff --git a/test/orange/RaytraceImager.test.cc b/test/orange/RaytraceImager.test.cc index 47303afe1b..734e16609f 100644 --- a/test/orange/RaytraceImager.test.cc +++ b/test/orange/RaytraceImager.test.cc @@ -180,8 +180,6 @@ template class RaytraceImagerTest : public OrangeGeoTestBase { protected: - using Initializer_t = GeoTrackInitializer; - real_type unit_length() const override { return 1; } void SetUp() override From 567576d1f94c6a318d0cc9c50ea0b64f7492f346 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Wed, 15 May 2024 10:36:55 -0500 Subject: [PATCH 48/59] Add option to specify max substeps in field propagator (#1236) --- app/celer-g4/GlobalSetup.cc | 1 + app/celer-sim/Runner.cc | 3 ++- src/accel/SetupOptions.hh | 5 ++++ src/accel/SetupOptionsMessenger.cc | 3 +++ src/accel/SetupOptionsMessenger.hh | 1 + src/accel/SharedParams.cc | 3 ++- src/celeritas/field/FieldDriver.hh | 5 ++++ src/celeritas/field/FieldDriverOptions.cc | 2 ++ src/celeritas/field/FieldDriverOptions.hh | 6 ++++- .../field/FieldDriverOptionsIO.json.cc | 2 ++ src/celeritas/field/FieldPropagator.hh | 25 ++++++++----------- src/celeritas/track/SimParams.cc | 25 ++++++++++++++----- src/celeritas/track/SimParams.hh | 7 +++++- 13 files changed, 64 insertions(+), 24 deletions(-) diff --git a/app/celer-g4/GlobalSetup.cc b/app/celer-g4/GlobalSetup.cc index 442fb77603..cd005ec3b6 100644 --- a/app/celer-g4/GlobalSetup.cc +++ b/app/celer-g4/GlobalSetup.cc @@ -168,6 +168,7 @@ void GlobalSetup::ReadInput(std::string const& filename) options_->action_times = input_.action_times; options_->default_stream = input_.default_stream; options_->auto_flush = input_.auto_flush; + options_->max_field_substeps = input_.field_options.max_substeps; } else if (ends_with(filename, ".mac")) { diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index d8f3dd4eeb..8284ada605 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -381,7 +381,8 @@ void Runner::build_core_params(RunnerInput const& inp, params.rng = std::make_shared(inp.seed); // Construct simulation params - params.sim = SimParams::from_import(imported, params.particle); + params.sim = SimParams::from_import( + imported, params.particle, inp.field_options.max_substeps); // Get the total number of events auto num_events = this->build_events(inp, params.particle); diff --git a/src/accel/SetupOptions.hh b/src/accel/SetupOptions.hh index 79c891fb97..e141a8ab90 100644 --- a/src/accel/SetupOptions.hh +++ b/src/accel/SetupOptions.hh @@ -138,6 +138,11 @@ struct SetupOptions AlongStepFactory make_along_step; //!@} + //!@{ + //! \name Field options + short int max_field_substeps{100}; + //!@} + //!@{ //! \name Sensitive detector options SDSetupOptions sd; diff --git a/src/accel/SetupOptionsMessenger.cc b/src/accel/SetupOptionsMessenger.cc index ffc648598f..3146394483 100644 --- a/src/accel/SetupOptionsMessenger.cc +++ b/src/accel/SetupOptionsMessenger.cc @@ -193,6 +193,9 @@ SetupOptionsMessenger::SetupOptionsMessenger(SetupOptions* options) add_cmd(&options->auto_flush, "autoFlush", "Number of tracks to buffer before offloading"); + add_cmd(&options->max_field_substeps, + "maxFieldSubsteps", + "Limit on substeps in the field propagator"); directories_.emplace_back(new CelerDirectory( "/celer/detector/", "Celeritas sensitive detector setup options")); diff --git a/src/accel/SetupOptionsMessenger.hh b/src/accel/SetupOptionsMessenger.hh index e4657f65f9..f7021e1176 100644 --- a/src/accel/SetupOptionsMessenger.hh +++ b/src/accel/SetupOptionsMessenger.hh @@ -34,6 +34,7 @@ struct SetupOptions; maxInitializers | Maximum number of track initializers secondaryStackFactor | At least the average number of secondaries per track autoFlush | Number of tracks to buffer before offloading + maxFieldSubsteps | Limit on substeps in field propagator * The following option is exposed in the \c /celer/detector/ command * "directory": diff --git a/src/accel/SharedParams.cc b/src/accel/SharedParams.cc index d3b18cd926..0a882de325 100644 --- a/src/accel/SharedParams.cc +++ b/src/accel/SharedParams.cc @@ -552,7 +552,8 @@ void SharedParams::initialize_core(SetupOptions const& options) params.rng = std::make_shared(CLHEP::HepRandom::getTheSeed()); // Construct simulation params - params.sim = SimParams::from_import(*imported, params.particle); + params.sim = SimParams::from_import( + *imported, params.particle, options.max_field_substeps); // Construct track initialization params params.init = [&options] { diff --git a/src/celeritas/field/FieldDriver.hh b/src/celeritas/field/FieldDriver.hh index b5b5e43043..d8bbec36eb 100644 --- a/src/celeritas/field/FieldDriver.hh +++ b/src/celeritas/field/FieldDriver.hh @@ -73,6 +73,11 @@ class FieldDriver //// ACCESSORS //// + CELER_FUNCTION short int max_substeps() const + { + return options_.max_substeps; + } + CELER_FUNCTION real_type minimum_step() const { return options_.minimum_step; diff --git a/src/celeritas/field/FieldDriverOptions.cc b/src/celeritas/field/FieldDriverOptions.cc index 3545c0b02a..d615d59185 100644 --- a/src/celeritas/field/FieldDriverOptions.cc +++ b/src/celeritas/field/FieldDriverOptions.cc @@ -44,6 +44,8 @@ void validate_input(FieldDriverOptions const& opts) << "invalid max_stepping_decrease " << opts.max_stepping_decrease); CELER_VALIDATE(opts.max_nsteps > 0, << "invalid max_nsteps " << opts.max_nsteps); + CELER_VALIDATE(opts.max_substeps > 0, + << "invalid max_substeps " << opts.max_substeps); CELER_ENSURE(opts); } diff --git a/src/celeritas/field/FieldDriverOptions.hh b/src/celeritas/field/FieldDriverOptions.hh index 429be1a4f0..7f912c040b 100644 --- a/src/celeritas/field/FieldDriverOptions.hh +++ b/src/celeritas/field/FieldDriverOptions.hh @@ -61,6 +61,9 @@ struct FieldDriverOptions //! Maximum number of integrations (or trials) short int max_nsteps = 100; + //! Maximum number of substeps in the field propagator + short int max_substeps = 100; + //! Initial step tolerance static constexpr inline real_type initial_step_tol = 1e-6; @@ -84,7 +87,7 @@ struct FieldDriverOptions && (safety > 0 && safety < 1) && (max_stepping_increase > 1) && (max_stepping_decrease > 0 && max_stepping_decrease < 1) - && (max_nsteps > 0); + && (max_nsteps > 0) && (max_substeps > 0); // clang-format on } }; @@ -107,6 +110,7 @@ operator==(FieldDriverOptions const& a, FieldDriverOptions const& b) && a.max_stepping_increase == b.max_stepping_increase && a.max_stepping_decrease == b.max_stepping_decrease && a.max_nsteps == b.max_nsteps + && a.max_substeps == b.max_substeps && a.initial_step_tol == b.initial_step_tol && a.dchord_tol == b.dchord_tol && a.min_chord_shrink == b.min_chord_shrink; diff --git a/src/celeritas/field/FieldDriverOptionsIO.json.cc b/src/celeritas/field/FieldDriverOptionsIO.json.cc index 9fca979be8..beee039233 100644 --- a/src/celeritas/field/FieldDriverOptionsIO.json.cc +++ b/src/celeritas/field/FieldDriverOptionsIO.json.cc @@ -41,6 +41,7 @@ void from_json(nlohmann::json const& j, FieldDriverOptions& opts) FDO_INPUT(max_stepping_increase); FDO_INPUT(max_stepping_decrease); FDO_INPUT(max_nsteps); + FDO_INPUT(max_substeps); #undef FDO_INPUT } @@ -64,6 +65,7 @@ void to_json(nlohmann::json& j, FieldDriverOptions const& opts) CELER_JSON_PAIR(opts, max_stepping_increase), CELER_JSON_PAIR(opts, max_stepping_decrease), CELER_JSON_PAIR(opts, max_nsteps), + CELER_JSON_PAIR(opts, max_substeps), }; } diff --git a/src/celeritas/field/FieldPropagator.hh b/src/celeritas/field/FieldPropagator.hh index f61432f108..167fcfbe72 100644 --- a/src/celeritas/field/FieldPropagator.hh +++ b/src/celeritas/field/FieldPropagator.hh @@ -21,16 +21,6 @@ namespace celeritas { -//---------------------------------------------------------------------------// -/*! - * Configuration options for the field propagator. - */ -struct FieldPropagatorOptions -{ - //! Limit on substeps - static constexpr short int max_substeps = 100; -}; - //---------------------------------------------------------------------------// /*! * Propagate a charged particle in a field. @@ -74,10 +64,7 @@ class FieldPropagator static CELER_CONSTEXPR_FUNCTION bool tracks_can_loop() { return true; } //! Limit on substeps - static CELER_CONSTEXPR_FUNCTION short int max_substeps() - { - return FieldPropagatorOptions::max_substeps; - } + inline CELER_FUNCTION short int max_substeps() const; // Intersection tolerance inline CELER_FUNCTION real_type delta_intersection() const; @@ -353,6 +340,16 @@ CELER_FUNCTION real_type FieldPropagator::delta_intersection() con return driver_.delta_intersection(); } +//---------------------------------------------------------------------------// +/*! + * Maximum number of substeps. + */ +template +CELER_FUNCTION short int FieldPropagator::max_substeps() const +{ + return driver_.max_substeps(); +} + //---------------------------------------------------------------------------// /*! * Distance to bump or to consider a "zero" movement. diff --git a/src/celeritas/track/SimParams.cc b/src/celeritas/track/SimParams.cc index cc8dfa3e3f..ab0e1de127 100644 --- a/src/celeritas/track/SimParams.cc +++ b/src/celeritas/track/SimParams.cc @@ -10,7 +10,7 @@ #include "corecel/Assert.hh" #include "corecel/data/CollectionBuilder.hh" #include "corecel/math/Algorithms.hh" -#include "celeritas/field/FieldPropagator.hh" +#include "celeritas/field/FieldDriverOptions.hh" #include "celeritas/io/ImportData.hh" #include "celeritas/phys/ParticleParams.hh" @@ -23,9 +23,12 @@ namespace celeritas * Construct with imported data. */ std::shared_ptr -SimParams::from_import(ImportData const& data, SPConstParticles particle_params) +SimParams::from_import(ImportData const& data, + SPConstParticles particle_params, + short int max_field_substeps) { CELER_EXPECT(particle_params); + CELER_EXPECT(max_field_substeps > 0); CELER_EXPECT(data.trans_params); CELER_EXPECT(data.trans_params.looping.size() == particle_params->size()); @@ -35,10 +38,9 @@ SimParams::from_import(ImportData const& data, SPConstParticles particle_params) // Calculate the maximum number of steps a track below the threshold energy // can take while looping (ceil(max Geant4 field propagator substeps / max // Celeritas field propagator substeps)) - size_type max_substeps = FieldPropagatorOptions::max_substeps; - size_type imported_max_substeps = data.trans_params.max_substeps; - CELER_ASSERT(imported_max_substeps >= max_substeps); - auto max_subthreshold_steps = ceil_div(imported_max_substeps, max_substeps); + CELER_ASSERT(data.trans_params.max_substeps >= max_field_substeps); + auto max_subthreshold_steps = ceil_div( + data.trans_params.max_substeps, max_field_substeps); for (auto pid : range(ParticleId{input.particles->size()})) { @@ -58,6 +60,17 @@ SimParams::from_import(ImportData const& data, SPConstParticles particle_params) return std::make_shared(input); } +//---------------------------------------------------------------------------// +/*! + * Construct with imported data and default max field substeps. + */ +std::shared_ptr +SimParams::from_import(ImportData const& data, SPConstParticles particle_params) +{ + return SimParams::from_import( + data, particle_params, FieldDriverOptions{}.max_substeps); +} + //---------------------------------------------------------------------------// /*! * Construct with simulation options. diff --git a/src/celeritas/track/SimParams.hh b/src/celeritas/track/SimParams.hh index 8a648c5157..a5d1bb864a 100644 --- a/src/celeritas/track/SimParams.hh +++ b/src/celeritas/track/SimParams.hh @@ -41,10 +41,15 @@ class SimParams final : public ParamsDataInterface }; public: - // Construct with imported data + // Construct with imported data and default max field substeps static std::shared_ptr from_import(ImportData const&, SPConstParticles); + // Construct with imported data + static std::shared_ptr from_import(ImportData const&, + SPConstParticles, + short int max_field_substeps); + // Construct with simulation input data explicit SimParams(Input const&); From 7c26ff78b37d6368dbfd8920e672d94bba818534 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 17 May 2024 15:06:53 -0400 Subject: [PATCH 49/59] Fix Spack CI (#1241) --- .github/workflows/build-spack.yml | 6 ++++-- scripts/spack.yaml | 14 +++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-spack.yml b/.github/workflows/build-spack.yml index 7c53acd4b4..3327772b25 100644 --- a/.github/workflows/build-spack.yml +++ b/.github/workflows/build-spack.yml @@ -8,7 +8,7 @@ concurrency: group: build-spack-${{github.ref}}-${{github.event.pull_request.number || github.run_number}}-${{github.workflow}} env: - SPACK_REF: 968ad02473b12f6305cc1fe19f2a0d706f171154 # develop-2024-03-10-70-g968ad02473 + SPACK_REF: 5fe93fee1eec46a0750bd340198bffcb92ff9eec # v0.22.0 jobs: spack: @@ -59,7 +59,7 @@ jobs: fetch-depth: 255 fetch-tags: true # to get version information - name: Setup Spack - uses: spack/setup-spack@5ab3c91bdefffffad9a7e45d1d156146afebb3a7 + uses: spack/setup-spack@0ce61628ed8a32a5664cf01a0e0b5a4834a3b413 # 2024/03 with: ref: ${{env.SPACK_REF}} buildcache: true @@ -67,6 +67,8 @@ jobs: path: spack-src - name: Initialize spack environment run: | + # TODO: https://github.com/spack/spack/pull/43136 + curl -LfsS https://github.com/spack/spack/pull/43136.patch | git -C spack-src apply - cp scripts/ci/spack.yaml . if [ "${{matrix.geometry}}" == "vecgeom" ]; then spack -e . add vecgeom diff --git a/scripts/spack.yaml b/scripts/spack.yaml index 13c3bc6733..9dedae71cb 100644 --- a/scripts/spack.yaml +++ b/scripts/spack.yaml @@ -2,7 +2,7 @@ spack: specs: - cmake - doxygen - - "geant4@11 cxxstd=17" + - "geant4@11" - git - git-lfs - "googletest@1.10:" @@ -16,9 +16,9 @@ spack: - py-sphinx - py-sphinxcontrib-bibtex - py-sphinxcontrib-mermaid - - "root@6.24: cxxstd=17" + - "root@6.28:" - "swig@4.1:" - - "vecgeom@1.2.4: +gdml cxxstd=17" + - "vecgeom@1.2.4: +gdml" view: true concretizer: unify: true @@ -27,10 +27,6 @@ spack: # Note: ~gsl and ~math are removed because dd4hep requires them variants: ~aqua ~davix ~examples ~opengl ~x ~tbb all: - providers: - blas: [openblas] - lapack: [openblas] - mpi: [openmpi] - # Uncomment to enable cuda build or run within the spack env: + # Note: for CUDA support run this command in your environment: # spack config add packages:all:variants:"cxxstd=17 +cuda cuda_arch=" - variants: cxxstd=17 # +cuda cuda_arch= + variants: cxxstd=17 From 1a0470f82dfbc67d3b26fa35a4d93909bb355b84 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 17 May 2024 15:49:20 -0400 Subject: [PATCH 50/59] Regularize quadric surfaces to deduplicate opposing surfaces (#1237) * Add exterior bounding box for gentrap * Add example showing failure to deduplicate * Un-inline full GQ/SQ constructors * Regularize signs on quadrics * Fix function decorator and add comments with regularization test --- scripts/cmake-presets/goldfinger.json | 2 +- src/orange/surf/GeneralQuadric.cc | 29 +++++++ src/orange/surf/GeneralQuadric.hh | 35 ++------ src/orange/surf/SimpleQuadric.cc | 22 +++++ src/orange/surf/SimpleQuadric.hh | 23 ----- src/orange/surf/SurfaceSimplifier.cc | 99 ++++++++++++++-------- src/orange/surf/SurfaceSimplifier.hh | 4 +- test/orange/orangeinp/ConvexRegion.test.cc | 85 ++++++++++++++++++- test/orange/surf/SurfaceSimplifier.test.cc | 35 +++++--- 9 files changed, 226 insertions(+), 108 deletions(-) diff --git a/scripts/cmake-presets/goldfinger.json b/scripts/cmake-presets/goldfinger.json index c8582ba1d1..dedb60c0c9 100644 --- a/scripts/cmake-presets/goldfinger.json +++ b/scripts/cmake-presets/goldfinger.json @@ -24,7 +24,7 @@ "CMAKE_CXX_STANDARD": {"type": "STRING", "value": "17"}, "CMAKE_CXX_EXTENSIONS": {"type": "BOOL", "value": "OFF"}, "CMAKE_FIND_FRAMEWORK": {"type": "STRING", "value": "LAST"}, - "CMAKE_INSTALL_PREFIX": "${sourceDir}/install-${presetName}" + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install-${presetName}", "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-O2 -g -DNDEBUG -fno-inline -fno-omit-frame-pointer", "CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wno-error=deprecated -pedantic -fdiagnostics-color=always" } diff --git a/src/orange/surf/GeneralQuadric.cc b/src/orange/surf/GeneralQuadric.cc index 2f75f20174..990661067f 100644 --- a/src/orange/surf/GeneralQuadric.cc +++ b/src/orange/surf/GeneralQuadric.cc @@ -7,10 +7,39 @@ //---------------------------------------------------------------------------// #include "GeneralQuadric.hh" +#include "corecel/Assert.hh" + #include "SimpleQuadric.hh" namespace celeritas { +//---------------------------------------------------------------------------// +/*! + * Construct with all coefficients. + * + * TODO: normalize so that largest eigenvalue is unity? Or what? (It would be + * nice to have "slightly twisted planes" have order-epsilon cross terms as + * opposed to order 1/eps linear terms.) + */ +GeneralQuadric::GeneralQuadric(Real3 const& abc, + Real3 const& def, + Real3 const& ghi, + real_type j) + : a_(abc[0]) + , b_(abc[1]) + , c_(abc[2]) + , d_(def[0]) + , e_(def[1]) + , f_(def[2]) + , g_(ghi[0]) + , h_(ghi[1]) + , i_(ghi[2]) + , j_(j) +{ + CELER_EXPECT(a_ != 0 || b_ != 0 || c_ != 0 || d_ != 0 || e_ != 0 || f_ != 0 + || g_ != 0 || h_ != 0 || i_ != 0); +} + //---------------------------------------------------------------------------// /*! * Promote from a simple quadric. diff --git a/src/orange/surf/GeneralQuadric.hh b/src/orange/surf/GeneralQuadric.hh index f52a67f0b7..dc33e47d97 100644 --- a/src/orange/surf/GeneralQuadric.hh +++ b/src/orange/surf/GeneralQuadric.hh @@ -59,11 +59,11 @@ class GeneralQuadric public: //// CONSTRUCTORS //// - // Construct with radius - explicit inline CELER_FUNCTION GeneralQuadric(Real3 const& abc, - Real3 const& def, - Real3 const& ghi, - real_type j); + // Construct from coefficients + explicit GeneralQuadric(Real3 const& abc, + Real3 const& def, + Real3 const& ghi, + real_type j); // Construct from raw data template @@ -114,31 +114,6 @@ class GeneralQuadric //---------------------------------------------------------------------------// // INLINE DEFINITIONS -//---------------------------------------------------------------------------// -/*! - * Construct with all coefficients. - * - * TODO: normalize? - */ -CELER_FUNCTION GeneralQuadric::GeneralQuadric(Real3 const& abc, - Real3 const& def, - Real3 const& ghi, - real_type j) - : a_(abc[0]) - , b_(abc[1]) - , c_(abc[2]) - , d_(def[0]) - , e_(def[1]) - , f_(def[2]) - , g_(ghi[0]) - , h_(ghi[1]) - , i_(ghi[2]) - , j_(j) -{ - CELER_EXPECT(a_ != 0 || b_ != 0 || c_ != 0 || d_ != 0 || e_ != 0 || f_ != 0 - || g_ != 0 || h_ != 0 || i_ != 0); -} - //---------------------------------------------------------------------------// /*! * Construct from raw data. diff --git a/src/orange/surf/SimpleQuadric.cc b/src/orange/surf/SimpleQuadric.cc index 90ac02e1b8..fd82c1eb2b 100644 --- a/src/orange/surf/SimpleQuadric.cc +++ b/src/orange/surf/SimpleQuadric.cc @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #include "SimpleQuadric.hh" +#include "corecel/Assert.hh" #include "corecel/cont/Range.hh" #include "corecel/math/Algorithms.hh" #include "corecel/math/ArrayOperators.hh" @@ -18,6 +19,27 @@ namespace celeritas { +//---------------------------------------------------------------------------// +/*! + * Construct with coefficients. + * + * The quadric is ill-defined if all non-constants are zero. + * + * TODO: normalize so that largest eigenvalue is unity? + */ +SimpleQuadric::SimpleQuadric(Real3 const& abc, Real3 const& def, real_type g) + : a_(abc[0]) + , b_(abc[1]) + , c_(abc[2]) + , d_(def[0]) + , e_(def[1]) + , f_(def[2]) + , g_(g) +{ + CELER_EXPECT(a_ != 0 || b_ != 0 || c_ != 0 || d_ != 0 || e_ != 0 + || f_ != 0); +} + //---------------------------------------------------------------------------// /*! * Promote from a plane. diff --git a/src/orange/surf/SimpleQuadric.hh b/src/orange/surf/SimpleQuadric.hh index b9481cbffe..e455778b54 100644 --- a/src/orange/surf/SimpleQuadric.hh +++ b/src/orange/surf/SimpleQuadric.hh @@ -62,7 +62,6 @@ class SimpleQuadric //// CONSTRUCTORS //// // Construct from coefficients - inline CELER_FUNCTION SimpleQuadric(Real3 const& abc, Real3 const& def, real_type g); // Construct from raw data @@ -120,28 +119,6 @@ class SimpleQuadric //---------------------------------------------------------------------------// // INLINE DEFINITIONS -//---------------------------------------------------------------------------// -/*! - * Construct with coefficients. - * - * The quadric is ill-defined if all non-constants are zero. - * - * TODO: normalize? - */ -CELER_FUNCTION -SimpleQuadric::SimpleQuadric(Real3 const& abc, Real3 const& def, real_type g) - : a_(abc[0]) - , b_(abc[1]) - , c_(abc[2]) - , d_(def[0]) - , e_(def[1]) - , f_(def[2]) - , g_(g) -{ - CELER_EXPECT(a_ != 0 || b_ != 0 || c_ != 0 || d_ != 0 || e_ != 0 - || f_ != 0); -} - //---------------------------------------------------------------------------// /*! * Construct from raw data. diff --git a/src/orange/surf/SurfaceSimplifier.cc b/src/orange/surf/SurfaceSimplifier.cc index a91ae5788c..f69fe7919c 100644 --- a/src/orange/surf/SurfaceSimplifier.cc +++ b/src/orange/surf/SurfaceSimplifier.cc @@ -58,6 +58,40 @@ class ZeroSnapper SoftZero<> soft_zero_; }; +struct SignCount +{ + int pos{0}; + int neg{0}; +}; + +SignCount count_signs(Span arr, real_type tol) +{ + SignCount result; + for (auto v : arr) + { + if (v < -tol) + ++result.neg; + else if (v > tol) + ++result.pos; + } + return result; +} + +template +S negate_coefficients(S const& orig) +{ + auto arr = make_array(orig.data()); + for (real_type& v : arr) + { + v = negate(v); + } + + // TODO: make_span doesn't use the correct overload and creates a + // dynamic extent span + using SpanT = decltype(orig.data()); + return S{SpanT{arr.data(), arr.size()}}; +} + //---------------------------------------------------------------------------// } // namespace @@ -236,9 +270,6 @@ auto SurfaceSimplifier::operator()(Sphere const& s) const //---------------------------------------------------------------------------// /*! * Simple quadric with near-zero terms can be another second-order surface. - * - * TODO: renormalize so that second-order terms are O(1) (and simplifying - * quadrics that are scaled by a constant)? */ auto SurfaceSimplifier::operator()(SimpleQuadric const& sq) -> Optional { // Determine possible simplifications by calculating number of zeros - int num_pos{0}; - int num_neg{0}; - for (auto v : sq.second()) - { - if (v < -tol_) - ++num_neg; - else if (v > tol_) - ++num_pos; - } + auto signs = count_signs(sq.second(), tol_); - if (num_pos == 0 && num_neg == 0) + if (signs.pos == 0 && signs.neg == 0) { // It's a plane return detail::QuadricPlaneConverter{tol_}(sq); } - else if (num_neg > num_pos) + else if (signs.neg > signs.pos) { - // Normalize sign so that it has more positive signs than negative - auto arr = make_array(sq.data()); - for (auto& v : arr) - { - v = negate(v); - } - - // Flip sense + // More positive signs than negative: + // flip the sense and reverse the values *sense_ = flip_sense(*sense_); - - // Construct reversed-sign quadric - // Todo: make_span doesn't use the correct overload and creates a - // dynamic extent span - return SimpleQuadric{ - Span{ - make_span(arr)}}; + return negate_coefficients(sq); } - else if (num_pos == 3) + else if (signs.pos == 3) { // Could be a sphere detail::QuadricSphereConverter to_sphere{tol_}; if (auto s = to_sphere(sq)) return *s; } - else if (num_pos == 2 && num_neg == 1) + else if (signs.pos == 2 && signs.neg == 1) { // Cone: one second-order term less than zero, others equal detail::QuadricConeConverter to_cone{tol_}; @@ -304,7 +315,7 @@ auto SurfaceSimplifier::operator()(SimpleQuadric const& sq) if (auto c = to_cone(AxisTag{}, sq)) return *c; } - else if (num_pos == 2 && num_neg == 0) + else if (signs.pos == 2 && signs.neg == 0) { // Cyl: one second-order term is zero, others are equal detail::QuadricCylConverter to_cyl{tol_}; @@ -322,21 +333,35 @@ auto SurfaceSimplifier::operator()(SimpleQuadric const& sq) //---------------------------------------------------------------------------// /*! - * Quadric with no cross terms is "simple". + * Quadric can be regularized or simplified. * - * TODO: guard against different-signed GQs? + * - When no cross terms are present, it's "simple". + * - When the higher-order terms are negative, the signs need to be flipped. */ auto SurfaceSimplifier::operator()(GeneralQuadric const& gq) - -> Optional + -> Optional { - auto cross = gq.cross(); - if (std::all_of(cross.begin(), cross.end(), SoftZero{tol_})) + // Cross term signs + auto csigns = count_signs(gq.cross(), tol_); + if (csigns.pos == 0 && csigns.neg == 0) { // No cross terms return SimpleQuadric{ make_array(gq.second()), make_array(gq.first()), gq.zeroth()}; } + // Second-order term signs + auto ssigns = count_signs(gq.second(), tol_); + if (ssigns.neg > ssigns.pos + || (ssigns.neg == 0 && ssigns.pos == 0 && csigns.neg > csigns.pos)) + { + // More positive signs than negative: + // flip the sense and reverse the values + *sense_ = flip_sense(*sense_); + return negate_coefficients(gq); + } + + // No simplification return {}; } diff --git a/src/orange/surf/SurfaceSimplifier.hh b/src/orange/surf/SurfaceSimplifier.hh index 57ca982536..7de6be915b 100644 --- a/src/orange/surf/SurfaceSimplifier.hh +++ b/src/orange/surf/SurfaceSimplifier.hh @@ -79,8 +79,8 @@ class SurfaceSimplifier SimpleQuadric> operator()(SimpleQuadric const&); - // Quadric with no cross terms is simple - Optional operator()(GeneralQuadric const&); + // Quadric can be normalized or simplified + Optional operator()(GeneralQuadric const&); //! Default: no simplifcation template diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index ac1ff715a3..82458e99fe 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -67,6 +67,8 @@ class ConvexRegionTest : public ::celeritas::test::Test return eval_sense(n); } + Unit const& unit() const { return unit_; } + private: Unit unit_; UnitBuilder unit_builder_{ @@ -744,14 +746,14 @@ TEST_F(GenTrapTest, full2) auto result = this->test(GenTrap::from_trap( 40, Turn{0.125}, Turn{0}, {20, 10, 10, 0.1}, {20, 10, 15, -0.2})); - static char const expected_node[] = "all(+0, -1, +2, -3, -4, -5)"; + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; static char const* const expected_surfaces[] = {"Plane: z=-40", "Plane: z=40", "Plane: y=-20", "GQuadric: {0,0,0} {0,0.0875,0} {40,-0.5,-41.25} -450", "Plane: y=20", - "GQuadric: {0,0,0} {0,-0.2125,0} {-40,-4.5,38.75} -450"}; + "GQuadric: {0,0,0} {0,0.2125,0} {40,4.5,-38.75} 450"}; EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); @@ -760,6 +762,85 @@ TEST_F(GenTrapTest, full2) EXPECT_VEC_SOFT_EQ((Real3{inf, 20, 40}), result.exterior.upper()); } +TEST_F(GenTrapTest, adjacent_twisted) +{ + /* Lower polygons: + * + * x=-1 x=1 + * +----+----+ y=1 + * | | | + * | L | R | + * | | | + * | | | + * +----+----+ y=-1 + * + * + * Upper polygons: + * x=-0.5 + * +--+------+ y=1 + * | \ | + * | \ R | + * | L \ | + * | \ | + * +-------+-+ y=-1 + * x=0.5 + */ + { + // Left + auto result + = this->test(GenTrap(1, + {{-1, -1}, {0, -1}, {0, 1}, {-1, 1}}, + {{-1, -1}, {0.5, -1}, {-0.5, 1}, {-1, 1}})); + + static char const expected_node[] = "all(+0, -1, +2, -3, -4, +5)"; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_SOFT_EQ((Real3{-1, -1, -1}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{inf, 1, 1}), result.exterior.upper()); + } + { + // Right + auto result + = this->test(GenTrap(1, + {{0, -1}, {1, -1}, {1, 1}, {0, 1}}, + {{0.5, -1}, {1, -1}, {1, 1}, {-0.5, 1}})); + + static char const expected_node[] = "all(+0, -1, +2, +3, -4, -6)"; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_SOFT_EQ((Real3{-inf, -1, -1}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{1, 1, 1}), result.exterior.upper()); + } + { + // Scaled (broadened) right side with the same hyperboloid but + // different size + // TODO: the scaled GQ should be normalized + auto result = this->test(GenTrap(1, + {{0, -2}, {2, -2}, {2, 2}, {0, 2}}, + {{1, -2}, {2, -2}, {2, 2}, {-1, 2}})); + static char const expected_node[] = "all(+0, -1, +7, -8, -9, +10)"; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_SOFT_EQ((Real3{-inf, -2, -1}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2, 2, 1}), result.exterior.upper()); + } + + static char const* const expected_surfaces[] = { + "Plane: z=-1", + "Plane: z=1", + "Plane: y=-1", + "GQuadric: {0,0,0} {0,0.5,0} {2,0.5,0} 0", + "Plane: y=1", + "Plane: x=-1", + "Plane: x=1", + "Plane: y=-2", + "Plane: x=2", + "Plane: y=2", + "GQuadric: {0,0,0} {0,1,0} {4,1,0} 0", + }; + EXPECT_VEC_EQ(expected_surfaces, surface_strings(this->unit())); +} + //---------------------------------------------------------------------------// // INFWEDGE //---------------------------------------------------------------------------// diff --git a/test/orange/surf/SurfaceSimplifier.test.cc b/test/orange/surf/SurfaceSimplifier.test.cc index e8405cec2c..560575c032 100644 --- a/test/orange/surf/SurfaceSimplifier.test.cc +++ b/test/orange/surf/SurfaceSimplifier.test.cc @@ -280,19 +280,28 @@ TEST_F(SurfaceSimplifierTest, simple_quadric) TEST_F(SurfaceSimplifierTest, general_quadric) { - this->check_unchanged( - GeneralQuadric{{10.3125, 22.9375, 15.75}, - {-21.867141445557, -20.25, 11.69134295109}, - {-11.964745962156, -9.1328585544429, -65.69134295109}, - 77.652245962156}); - - this->check_simplifies_to( - GeneralQuadric{{-2, 0, -2}, {0, 0, 0}, {4, 0, 12}, -2 * 3.75}, - SimpleQuadric{{-2, 0, -2}, {4, 0, 12}, -2 * 3.75}); - - this->check_simplifies_to( - GeneralQuadric{{1, 2, 3}, {0, 0, 0}, {-1, -1, 1}, 6}, - SimpleQuadric{{1, 2, 3}, {-1, -1, 1}, 6}); + { + SCOPED_TRACE("Rotated ellipsoid"); + this->check_unchanged(GeneralQuadric{ + {10.3125, 22.9375, 15.75}, + {-21.867141445557, -20.25, 11.69134295109}, + {-11.964745962156, -9.1328585544429, -65.69134295109}, + 77.652245962156}); + } + { + SCOPED_TRACE("Axis-aligned ellipsoid"); + this->check_simplifies_to( + GeneralQuadric{{-2, 0, -2}, {0, 0, 0}, {4, 0, 12}, -2 * 3.75}, + SimpleQuadric{{-2, 0, -2}, {4, 0, 12}, -2 * 3.75}); + } + { + this->check_unchanged( + GeneralQuadric{{0, 0, 0}, {0, 0.5, 0}, {2, 0.5, 0}, 0}); + this->check_simplifies_to( + GeneralQuadric{{0, 0, 0}, {0, -0.5, 0}, {-2, -0.5, 0}, 0}, + GeneralQuadric{{0, 0, 0}, {0, 0.5, 0}, {2, 0.5, 0}, 0}, + Sense::outside); + } } //---------------------------------------------------------------------------// From b0ff376e10fa7e813ac61b62ccad4553f7a056b3 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Sun, 19 May 2024 14:44:01 -0500 Subject: [PATCH 51/59] Update single and multiple Coulomb scattering configurations in `GeantPhysicsList` (#1239) --- app/celer-sim/simple-driver.py | 2 +- doc/main/overview.rst | 123 +++++++++++------- src/celeritas/ext/GeantPhysicsOptions.cc | 6 +- src/celeritas/ext/GeantPhysicsOptions.hh | 10 +- src/celeritas/ext/detail/GeantPhysicsList.cc | 96 ++++++++------ .../ext/detail/GeantProcessImporter.cc | 6 +- test/celeritas/ext/GeantImporter.test.cc | 6 +- 7 files changed, 149 insertions(+), 100 deletions(-) diff --git a/app/celer-sim/simple-driver.py b/app/celer-sim/simple-driver.py index 373617b184..a1670814e8 100755 --- a/app/celer-sim/simple-driver.py +++ b/app/celer-sim/simple-driver.py @@ -44,7 +44,7 @@ def strtobool(text): 'ionization': True, 'annihilation': True, 'brems': "all", - 'msc': "urban_extended" if core_geo == "vecgeom" else "none", + 'msc': "urban" if core_geo == "vecgeom" else "none", 'eloss_fluctuation': True, 'lpm': True, } diff --git a/doc/main/overview.rst b/doc/main/overview.rst index 55f9f01ef0..4509ce76d3 100644 --- a/doc/main/overview.rst +++ b/doc/main/overview.rst @@ -53,35 +53,39 @@ and their corresponding Geant4 classes are documented in :ref:`celeritas_physics .. table:: Electromagnetic physics processes and models available in Celeritas. - +----------------+---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | **Particle** | **Processes** | **Models** | **Celeritas Implementation** | **Applicability** | - +----------------+---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | :math:`e^-` | Ionisation | Møller | :cpp:class:`celeritas::MollerBhabhaInteractor` | 0--100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Bremsstrahlung | Seltzer--Berger | :cpp:class:`celeritas::SeltzerBergerInteractor` | 0--1 GeV | - | | +-----------------------------+----------------------------------------------------+--------------------------+ - | | | Relativistic | :cpp:class:`celeritas::RelativisticBremInteractor` | 1 GeV -- 100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Coulomb scattering | Urban | :cpp:class:`celeritas::UrbanMscScatter` | 100 eV -- 100 TeV | - +----------------+---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | :math:`e^+` | Ionisation | Bhabha | :cpp:class:`celeritas::MollerBhabhaInteractor` | 0--100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Bremsstrahlung | Seltzer-Berger | :cpp:class:`celeritas::SeltzerBergerInteractor` | 0--1 GeV | - | | +-----------------------------+----------------------------------------------------+--------------------------+ - | | | Relativistic | :cpp:class:`celeritas::RelativisticBremInteractor` | 1 GeV -- 100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Coulomb scattering | Urban | :cpp:class:`celeritas::UrbanMscScatter` | 100 eV -- 100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Annihilation | :math:`e^+,e^- \to 2\gamma` | :cpp:class:`celeritas::EPlusGGInteractor` | 0--100 TeV | - +----------------+---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | :math:`\gamma` | Photoelectric | Livermore | :cpp:class:`celeritas::LivermorePEInteractor` | 0--100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Compton scattering | Klein--Nishina | :cpp:class:`celeritas::KleinNishinaInteractor` | 0--100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Pair production | Bethe--Heitler | :cpp:class:`celeritas::BetheHeitlerInteractor` | 0--100 TeV | - | +---------------------+-----------------------------+----------------------------------------------------+--------------------------+ - | | Rayleigh scattering | Livermore | :cpp:class:`celeritas::RayleighInteractor` | 0--100 TeV | - +----------------+---------------------+-----------------------------+----------------------------------------------------+--------------------------+ + +----------------+---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | **Particle** | **Processes** | **Models** | **Celeritas Implementation** | **Applicability** | + +----------------+---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | :math:`e^-` | Ionisation | Møller | :cpp:class:`celeritas::MollerBhabhaInteractor` | 0--100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Bremsstrahlung | Seltzer--Berger | :cpp:class:`celeritas::SeltzerBergerInteractor` | 0--1 GeV | + | | +-----------------------------+-----------------------------------------------------+--------------------------+ + | | | Relativistic | :cpp:class:`celeritas::RelativisticBremInteractor` | 1 GeV -- 100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Coulomb scattering | Urban | :cpp:class:`celeritas::UrbanMscScatter` | 100 eV -- 100 TeV | + | | +-----------------------------+-----------------------------------------------------+--------------------------+ + | | | Coulomb | :cpp:class:`celeritas::CoulombScatteringInteractor` | 0--100 TeV | + +----------------+---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | :math:`e^+` | Ionisation | Bhabha | :cpp:class:`celeritas::MollerBhabhaInteractor` | 0--100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Bremsstrahlung | Seltzer-Berger | :cpp:class:`celeritas::SeltzerBergerInteractor` | 0--1 GeV | + | | +-----------------------------+-----------------------------------------------------+--------------------------+ + | | | Relativistic | :cpp:class:`celeritas::RelativisticBremInteractor` | 1 GeV -- 100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Coulomb scattering | Urban | :cpp:class:`celeritas::UrbanMscScatter` | 100 eV -- 100 TeV | + | | +-----------------------------+-----------------------------------------------------+--------------------------+ + | | | Coulomb | :cpp:class:`celeritas::CoulombScatteringInteractor` | 0--100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Annihilation | :math:`e^+,e^- \to 2\gamma` | :cpp:class:`celeritas::EPlusGGInteractor` | 0--100 TeV | + +----------------+---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | :math:`\gamma` | Photoelectric | Livermore | :cpp:class:`celeritas::LivermorePEInteractor` | 0--100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Compton scattering | Klein--Nishina | :cpp:class:`celeritas::KleinNishinaInteractor` | 0--100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Pair production | Bethe--Heitler | :cpp:class:`celeritas::BetheHeitlerInteractor` | 0--100 TeV | + | +---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ + | | Rayleigh scattering | Livermore | :cpp:class:`celeritas::RayleighInteractor` | 0--100 TeV | + +----------------+---------------------+-----------------------------+-----------------------------------------------------+--------------------------+ .. only:: latex @@ -92,33 +96,38 @@ and their corresponding Geant4 classes are documented in :ref:`celeritas_physics \begin{threeparttable} \begin{tabular}{| l | l | l | l | r | } \hline - \textbf{Particle} & \textbf{Processes} & \textbf{Models} & \textbf{Celeritas Implementation} & \textbf{Applicability} \\ + \textbf{Particle} & \textbf{Processes} & \textbf{Models} & \textbf{Celeritas Implementation} & \textbf{Applicability} \\ \hline - \multirow{4}{*}{$e^-$} & Ionisation & Møller & \texttt{\scriptsize celeritas::MollerBhabhaInteractor} & 0--100 TeV \\ + \multirow{4}{*}{$e^-$} & Ionisation & Møller & \texttt{\scriptsize celeritas::MollerBhabhaInteractor} & 0--100 TeV \\ \cline{2-5} - & \multirow{2}{*}{Bremsstrahlung} & Seltzer--Berger & \texttt{\scriptsize celeritas::SeltzerBergerInteractor} & 0--1 GeV \\ - \cline{3-5} - & & Relativistic & \texttt{\scriptsize celeritas::RelativisticBremInteractor} & 1 GeV -- 100 TeV \\ + & \multirow{2}{*}{Bremsstrahlung} & Seltzer--Berger & \texttt{\scriptsize celeritas::SeltzerBergerInteractor} & 0--1 GeV \\ + \cline{3-5} + & & Relativistic & \texttt{\scriptsize celeritas::RelativisticBremInteractor} & 1 GeV -- 100 TeV \\ + \cline{2-5} + & \multirow{2}{*}{Coulomb scattering} & Urban & \texttt{\scriptsize celeritas::UrbanMscScatter} & 100 eV -- 100 TeV \\ + \cline{3-5} + & & Coulomb & \texttt{\scriptsize celeritas::CoulombScatteringInteractor} & 0--100 TeV \\ \cline{2-5} - & Coulomb scattering & Urban & \texttt{\scriptsize celeritas::UrbanMscScatter} & 100 eV -- 100 TeV \\ \hline - \multirow{5}{*}{$e^+$} & Ionisation & Bhabha & \texttt{\scriptsize celeritas::MollerBhabhaInteractor} & 0--100 TeV \\ + \multirow{5}{*}{$e^+$} & Ionisation & Bhabha & \texttt{\scriptsize celeritas::MollerBhabhaInteractor} & 0--100 TeV \\ \cline{2-5} - & \multirow{2}{*}{Bremsstrahlung} & Seltzer--Berger & \texttt{\scriptsize celeritas::SeltzerBergerInteractor} & 0--1 GeV \\ - \cline{3-5} - & & Relativistic & \texttt{\scriptsize celeritas::RelativisticBremInteractor} & 1 GeV -- 100 TeV \\ + & \multirow{2}{*}{Bremsstrahlung} & Seltzer--Berger & \texttt{\scriptsize celeritas::SeltzerBergerInteractor} & 0--1 GeV \\ + \cline{3-5} + & & Relativistic & \texttt{\scriptsize celeritas::RelativisticBremInteractor} & 1 GeV -- 100 TeV \\ \cline{2-5} - & Coulomb scattering & Urban & \texttt{\scriptsize celeritas::UrbanMscScatter} & 100 eV -- 100 TeV \\ + & \multirow{2}{*}{Coulomb scattering} & Urban & \texttt{\scriptsize celeritas::UrbanMscScatter} & 100 eV -- 100 TeV \\ + \cline{3-5} + & & Coulomb & \texttt{\scriptsize celeritas::CoulombScatteringInteractor} & 0--100 TeV \\ \cline{2-5} - & Annihilation & $e^+,e^-\to 2\gamma$ & \texttt{\scriptsize celeritas::EPlusGGInteractor} & 0--100 TeV \\ + & Annihilation & $e^+,e^-\to 2\gamma$ & \texttt{\scriptsize celeritas::EPlusGGInteractor} & 0--100 TeV \\ \hline - \multirow{4}{*}{$\gamma$} & Photoelectric & Livermore & \texttt{\scriptsize celeritas::LivermorePEInteractor} & 0--100 TeV \\ + \multirow{4}{*}{$\gamma$} & Photoelectric & Livermore & \texttt{\scriptsize celeritas::LivermorePEInteractor} & 0--100 TeV \\ \cline{2-5} - & Compton scattering & Klein--Nishina & \texttt{\scriptsize celeritas::KleinNishinaInteractor} & 0--100 TeV \\ + & Compton scattering & Klein--Nishina & \texttt{\scriptsize celeritas::KleinNishinaInteractor} & 0--100 TeV \\ \cline{2-5} - & Pair production & Bethe--Heitler & \texttt{\scriptsize celeritas::BetheHeitlerInteractor} & 0--100 TeV \\ + & Pair production & Bethe--Heitler & \texttt{\scriptsize celeritas::BetheHeitlerInteractor} & 0--100 TeV \\ \cline{2-5} - & Rayleigh scattering & Livermore & \texttt{\scriptsize celeritas::RayleighInteractor} & 0--100 TeV \\ + & Rayleigh scattering & Livermore & \texttt{\scriptsize celeritas::RayleighInteractor} & 0--100 TeV \\ \hline \end{tabular} \end{threeparttable} @@ -137,6 +146,30 @@ The implemented physics models are meant to match the defaults constructed in ``G4EmParameters``, but some custom model cutoffs are not accessible to Celeritas. +Coulomb scattering +------------------ + +Elastic scattering of charged particles can be simulated in three ways: + +* A detailed single scattering model in which each scattering interaction is + sampled +* A multiple scattering approach which calculates global effects from many + collisions +* A combination of the two + +Though it is the most accurate, the single Coulomb scattering model is too +computationally expensive to be used in most applications as the number of +collisions can be extremely large. Instead, a "condensed" simulation algorithm +is typically used to determine the net energy loss, displacement, and direction +change from many collisions after a given path length. The Urban model is the +default multiple scattering model in Celeritas for all energies and in Geant4 +below 100 MeV. A third "mixed" simulation approach uses multiple scattering to +simulate interactions with scattering angles below a given polar angle limit +and single scattering for large angles. The Wentzel VI model, used together +with the single Coulomb scattering model, is an implementation of the mixed +simulation algorithm. It is the default model in Geant4 above 100 MeV and +currently under development in Celeritas. + Geometry ======== diff --git a/src/celeritas/ext/GeantPhysicsOptions.cc b/src/celeritas/ext/GeantPhysicsOptions.cc index c23923a3d3..ce6a8f3f42 100644 --- a/src/celeritas/ext/GeantPhysicsOptions.cc +++ b/src/celeritas/ext/GeantPhysicsOptions.cc @@ -35,10 +35,8 @@ char const* to_cstring(MscModelSelection value) static EnumStringMapper const to_cstring_impl{ "none", "urban", - "urban_extended", - "wentzel_vi", - "urban_wentzel", - "goudsmit_saunderson", + "wentzelvi", + "urban_wentzelvi", }; return to_cstring_impl(value); } diff --git a/src/celeritas/ext/GeantPhysicsOptions.hh b/src/celeritas/ext/GeantPhysicsOptions.hh index 85281dfed4..30a1fb0f87 100644 --- a/src/celeritas/ext/GeantPhysicsOptions.hh +++ b/src/celeritas/ext/GeantPhysicsOptions.hh @@ -28,11 +28,9 @@ enum class BremsModelSelection enum class MscModelSelection { none, - urban, - urban_extended, //!< Use 100 TeV as upper bound instead of 100 MeV - wentzel_vi, - urban_wentzel, //!< Urban for low-E, Wentzel_VI for high-E - goudsmit_saunderson, + urban, //!< Urban for all energies + wentzelvi, //!< Wentzel VI for all energies + urban_wentzelvi, //!< Urban below 100 MeV, Wentzel VI above size_ }; @@ -82,7 +80,7 @@ struct GeantPhysicsOptions //! Enable bremsstrahlung and select a model BremsModelSelection brems{BremsModelSelection::all}; //! Enable multiple coulomb scattering and select a model - MscModelSelection msc{MscModelSelection::urban_extended}; + MscModelSelection msc{MscModelSelection::urban}; //! Enable atomic relaxation and select a model RelaxationSelection relaxation{RelaxationSelection::none}; //!@} diff --git a/src/celeritas/ext/detail/GeantPhysicsList.cc b/src/celeritas/ext/detail/GeantPhysicsList.cc index b46a4fdc19..4da195707a 100644 --- a/src/celeritas/ext/detail/GeantPhysicsList.cc +++ b/src/celeritas/ext/detail/GeantPhysicsList.cc @@ -107,11 +107,6 @@ GeantPhysicsList::GeantPhysicsList(Options const& options) : options_(options) em_parameters.SetLowestElectronEnergy( value_as(options.lowest_electron_energy) * CLHEP::MeV); - if (options_.msc == MscModelSelection::urban_extended) - { - CELER_LOG(debug) << "Extended low-energy MSC limit to 100 TeV"; - em_parameters.SetMscEnergyLimit(100 * CLHEP::TeV); - } em_parameters.SetApplyCuts(options.apply_cuts); this->SetDefaultCutValue( native_value_to(options.default_cutoff).value()); @@ -340,58 +335,79 @@ void GeantPhysicsList::add_e_processes(G4ParticleDefinition* p) } } + using MMS = MscModelSelection; + + // Energy limit between MSC models when multiple models are used + double msc_energy_limit = G4EmParameters::Instance()->MscEnergyLimit(); + bool set_energy_limit = options_.msc == MMS::urban_wentzelvi; + if (options_.coulomb_scattering) { // Coulomb scattering: G4eCoulombScatteringModel - double msc_energy_limit = G4EmParameters::Instance()->MscEnergyLimit(); - - auto process = std::make_unique(); - auto model = std::make_unique(); - process->SetMinKinEnergy(msc_energy_limit); - model->SetLowEnergyLimit(msc_energy_limit); - model->SetActivationLowEnergyLimit(msc_energy_limit); - process->SetEmModel(model.release()); - physics_list->RegisterProcess(process.release(), p); + if (options_.msc == MMS::urban) + { + CELER_LOG(warning) + << "Urban multiple scattering is used for all " + "energies: disabling G4eCoulombScatteringModel"; + } + else + { + auto process = std::make_unique(); + auto model = std::make_unique( + /* isCombined = */ options_.msc != MMS::none); + if (set_energy_limit) + { + process->SetMinKinEnergy(msc_energy_limit); + model->SetLowEnergyLimit(msc_energy_limit); + model->SetActivationLowEnergyLimit(msc_energy_limit); + } + + CELER_LOG(debug) << "Loaded single Coulomb scattering with " + "G4eCoulombScatteringModel from " + << model->LowEnergyLimit() << " MeV to " + << model->HighEnergyLimit() << " MeV"; + + process->SetEmModel(model.release()); + physics_list->RegisterProcess(process.release(), p); + } } - if (options_.msc != MscModelSelection::none) + if (options_.msc != MMS::none) { - // Multiple scattering: Urban (low E) and WentzelVI (high E) models - double msc_energy_limit = G4EmParameters::Instance()->MscEnergyLimit(); - auto process = std::make_unique(); - if (options_.msc == MscModelSelection::urban - || options_.msc == MscModelSelection::urban_extended - || options_.msc == MscModelSelection::urban_wentzel) + if (options_.msc == MMS::urban || options_.msc == MMS::urban_wentzelvi) { + // Multiple scattering: Urban auto model = std::make_unique(); - model->SetHighEnergyLimit(msc_energy_limit); - process->SetEmModel(model.release()); + if (set_energy_limit) + { + model->SetHighEnergyLimit(msc_energy_limit); + } - CELER_LOG(debug) << "Loaded low-energy multiple scattering with " - "G4UrbanMscModel"; - } + CELER_LOG(debug) << "Loaded multiple scattering with " + "G4UrbanMscModel from " + << model->LowEnergyLimit() << " MeV to " + << model->HighEnergyLimit() << " MeV"; - if (options_.msc == MscModelSelection::wentzel_vi - || options_.msc == MscModelSelection::urban_wentzel) - { - auto model = std::make_unique(); - model->SetLowEnergyLimit(msc_energy_limit); process->SetEmModel(model.release()); - - CELER_LOG(debug) << "Loaded high-energy multiple scattering with " - "G4WentzelVIModel"; } - if (options_.msc == MscModelSelection::goudsmit_saunderson) + if (options_.msc == MMS::wentzelvi + || options_.msc == MMS::urban_wentzelvi) { - // Multiple scattering: Goudsmit-Saunderson (low E) - auto model = std::make_unique(); - process->SetEmModel(model.release()); - + // Multiple scattering: WentzelVI + auto model = std::make_unique(); + if (set_energy_limit) + { + model->SetLowEnergyLimit(msc_energy_limit); + } CELER_LOG(debug) << "Loaded multiple scattering with " - "G4GoudsmitSaundersonMscModel"; + "G4WentzelVIModel from " + << model->LowEnergyLimit() << " MeV to " + << model->HighEnergyLimit() << " MeV"; + + process->SetEmModel(model.release()); } physics_list->RegisterProcess(process.release(), p); diff --git a/src/celeritas/ext/detail/GeantProcessImporter.cc b/src/celeritas/ext/detail/GeantProcessImporter.cc index e095d629bb..4baed88e37 100644 --- a/src/celeritas/ext/detail/GeantProcessImporter.cc +++ b/src/celeritas/ext/detail/GeantProcessImporter.cc @@ -382,6 +382,10 @@ GeantProcessImporter::operator()(G4ParticleDefinition const& particle, { if (G4VEmModel* model = process.GetModelByIndex(i)) { + CELER_LOG(debug) << "Saving MSC model '" << model->GetName() + << "' for particle " << particle.GetParticleName() + << " (" << particle.GetPDGEncoding() << ")"; + ImportMscModel imm; imm.particle_pdg = primary_pdg; try @@ -391,7 +395,7 @@ GeantProcessImporter::operator()(G4ParticleDefinition const& particle, } catch (celeritas::RuntimeError const&) { - CELER_LOG(warning) << "Encountered unknown process '" + CELER_LOG(warning) << "Encountered unknown MSC model '" << model->GetName() << "'"; imm.model_class = ImportModelClass::other; } diff --git a/test/celeritas/ext/GeantImporter.test.cc b/test/celeritas/ext/GeantImporter.test.cc index 1ba07e9827..d14b6f4ff7 100644 --- a/test/celeritas/ext/GeantImporter.test.cc +++ b/test/celeritas/ext/GeantImporter.test.cc @@ -256,7 +256,7 @@ class FourSteelSlabsEmStandard : public GeantImporterTest { nlohmann::json out = opts; EXPECT_JSON_EQ( - R"json({"annihilation":true,"apply_cuts":false,"brems":"all","compton_scattering":true,"coulomb_scattering":false,"default_cutoff":0.1,"eloss_fluctuation":true,"em_bins_per_decade":7,"gamma_conversion":true,"gamma_general":false,"integral_approach":true,"ionization":true,"linear_loss_limit":0.01,"lowest_electron_energy":[0.001,"MeV"],"lpm":true,"max_energy":[100000000.0,"MeV"],"min_energy":[0.0001,"MeV"],"msc":"urban_extended","msc_lambda_limit":0.1,"msc_range_factor":0.04,"msc_safety_factor":0.6,"msc_step_algorithm":"safety","photoelectric":true,"rayleigh_scattering":true,"relaxation":"all","verbose":true})json", + R"json({"annihilation":true,"apply_cuts":false,"brems":"all","compton_scattering":true,"coulomb_scattering":false,"default_cutoff":0.1,"eloss_fluctuation":true,"em_bins_per_decade":7,"gamma_conversion":true,"gamma_general":false,"integral_approach":true,"ionization":true,"linear_loss_limit":0.01,"lowest_electron_energy":[0.001,"MeV"],"lpm":true,"max_energy":[100000000.0,"MeV"],"min_energy":[0.0001,"MeV"],"msc":"urban","msc_lambda_limit":0.1,"msc_range_factor":0.04,"msc_safety_factor":0.6,"msc_step_algorithm":"safety","photoelectric":true,"rayleigh_scattering":true,"relaxation":"all","verbose":true})json", std::string(out.dump())); } #endif @@ -295,7 +295,7 @@ class OneSteelSphere : public GeantImporterTest GeantPhysicsOptions build_geant_options() const override { GeantPhysicsOptions opts; - opts.msc = MscModelSelection::urban_wentzel; + opts.msc = MscModelSelection::urban_wentzelvi; opts.relaxation = RelaxationSelection::none; opts.verbose = false; return opts; @@ -318,7 +318,7 @@ class OneSteelSphereGG : public OneSteelSphere { auto opts = OneSteelSphere::build_geant_options(); opts.gamma_general = true; - opts.msc = MscModelSelection::urban_extended; + opts.msc = MscModelSelection::urban; return opts; } }; From 91a8f5a60392045580ca323e6bf39051f7da935a Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Sun, 19 May 2024 23:59:05 -0500 Subject: [PATCH 52/59] Add custom FTFP BERT physics list with Celeritas EM physics constructor (#1242) * Add Celeritas FTFP BERT physics list with Celeritas-supported EM standard physics * Add celer-g4 option to use Celeritas custom FTFP BERT * Rename GeantPhysicsList to CelerEmPhysicsList * Add physics constructor for EM standard physics not supported in Celeritas * Implement G4EmBuilder methods for muon/hadron Em standard physics G4EmBuilder was added in Geant4@10.7.0 --- app/celer-g4/RunInput.cc | 3 +- app/celer-g4/RunInput.hh | 5 +- app/celer-g4/RunInputIO.json.cc | 6 +- app/celer-g4/celer-g4.cc | 47 +-- app/celer-g4/test-harness.py | 2 +- src/celeritas/CMakeLists.txt | 5 +- src/celeritas/ext/GeantSetup.cc | 5 +- .../ext/detail/CelerEmPhysicsList.cc | 39 +++ .../ext/detail/CelerEmPhysicsList.hh | 37 +++ ...ysicsList.cc => CelerEmStandardPhysics.cc} | 29 +- ...ysicsList.hh => CelerEmStandardPhysics.hh} | 10 +- src/celeritas/ext/detail/CelerFTFPBert.cc | 82 ++++++ src/celeritas/ext/detail/CelerFTFPBert.hh | 37 +++ .../ext/detail/MuHadEmStandardPhysics.cc | 272 ++++++++++++++++++ .../ext/detail/MuHadEmStandardPhysics.hh | 38 +++ 15 files changed, 564 insertions(+), 53 deletions(-) create mode 100644 src/celeritas/ext/detail/CelerEmPhysicsList.cc create mode 100644 src/celeritas/ext/detail/CelerEmPhysicsList.hh rename src/celeritas/ext/detail/{GeantPhysicsList.cc => CelerEmStandardPhysics.cc} (95%) rename src/celeritas/ext/detail/{GeantPhysicsList.hh => CelerEmStandardPhysics.hh} (82%) create mode 100644 src/celeritas/ext/detail/CelerFTFPBert.cc create mode 100644 src/celeritas/ext/detail/CelerFTFPBert.hh create mode 100644 src/celeritas/ext/detail/MuHadEmStandardPhysics.cc create mode 100644 src/celeritas/ext/detail/MuHadEmStandardPhysics.hh diff --git a/app/celer-g4/RunInput.cc b/app/celer-g4/RunInput.cc index 67b1e74e02..b5bfde0749 100644 --- a/app/celer-g4/RunInput.cc +++ b/app/celer-g4/RunInput.cc @@ -22,7 +22,8 @@ char const* to_cstring(PhysicsListSelection value) { static EnumStringMapper const to_cstring_impl{ "ftfp_bert", - "geant_physics_list", + "celer_ftfp_bert", + "celer_em", }; return to_cstring_impl(value); } diff --git a/app/celer-g4/RunInput.hh b/app/celer-g4/RunInput.hh index 9cd844c6cd..ac7ad440c6 100644 --- a/app/celer-g4/RunInput.hh +++ b/app/celer-g4/RunInput.hh @@ -24,7 +24,8 @@ namespace app enum class PhysicsListSelection { ftfp_bert, - geant_physics_list, + celer_ftfp_bert, //!< FTFP BERT with Celeritas EM standard physics + celer_em, //!< Celeritas EM standard physics only size_, }; @@ -72,7 +73,7 @@ struct RunInput bool default_stream{false}; //!< Launch all kernels on the default stream // Physics setup options - PhysicsListSelection physics_list{PhysicsListSelection::ftfp_bert}; + PhysicsListSelection physics_list{PhysicsListSelection::celer_ftfp_bert}; GeantPhysicsOptions physics_options; // Field setup options diff --git a/app/celer-g4/RunInputIO.json.cc b/app/celer-g4/RunInputIO.json.cc index b30f5e6d7a..537442da5f 100644 --- a/app/celer-g4/RunInputIO.json.cc +++ b/app/celer-g4/RunInputIO.json.cc @@ -131,10 +131,10 @@ void from_json(nlohmann::json const& j, RunInput& v) CELER_VALIDATE(v.event_file.empty() == static_cast(v.primary_options), << "either a HepMC3 filename or options to generate " "primaries must be provided (but not both)"); - CELER_VALIDATE(v.physics_list == PhysicsListSelection::geant_physics_list + CELER_VALIDATE(v.physics_list != PhysicsListSelection::ftfp_bert || !j.contains("physics_options"), << "'physics_options' can only be specified for " - "'geant_physics_list'"); + "'celer_ftfp_bert' or 'celer_em'"); CELER_VALIDATE((v.field != RunInput::no_field() || v.field_type == "rzmap") || !j.contains("field_options"), << "'field_options' cannot be specified without providing " @@ -176,7 +176,7 @@ void to_json(nlohmann::json& j, RunInput const& v) RI_SAVE(auto_flush); RI_SAVE(physics_list); - if (v.physics_list == PhysicsListSelection::geant_physics_list) + if (v.physics_list != PhysicsListSelection::ftfp_bert) { RI_SAVE(physics_options); } diff --git a/app/celer-g4/celer-g4.cc b/app/celer-g4/celer-g4.cc index 56216a4710..fcb67cef4e 100644 --- a/app/celer-g4/celer-g4.cc +++ b/app/celer-g4/celer-g4.cc @@ -55,7 +55,8 @@ #include "geocel/ScopedGeantLogger.hh" #include "celeritas/ext/GeantPhysicsOptions.hh" #include "celeritas/ext/ScopedRootErrorHandler.hh" -#include "celeritas/ext/detail/GeantPhysicsList.hh" +#include "celeritas/ext/detail/CelerEmPhysicsList.hh" +#include "celeritas/ext/detail/CelerFTFPBert.hh" #include "accel/SharedParams.hh" #include "ActionInitialization.hh" @@ -171,29 +172,35 @@ void run(int argc, char** argv, std::shared_ptr params) } setup.SetIgnoreProcesses(ignore_processes); - // Construct geometry, SD factory, physics, actions + // Construct geometry and SD factory run_manager->SetUserInitialization(new DetectorConstruction{params}); - switch (setup.input().physics_list) + + // Construct physics + if (setup.input().physics_list == PhysicsListSelection::ftfp_bert) { - case PhysicsListSelection::ftfp_bert: { - run_manager->SetUserInitialization( - new FTFP_BERT{/* verbosity = */ 0}); - break; + auto pl = std::make_unique(/* verbosity = */ 0); + run_manager->SetUserInitialization(pl.release()); + } + else + { + auto opts = setup.GetPhysicsOptions(); + if (std::find(ignore_processes.begin(), ignore_processes.end(), "Rayl") + != ignore_processes.end()) + { + opts.rayleigh_scattering = false; + } + if (setup.input().physics_list == PhysicsListSelection::celer_ftfp_bert) + { + // FTFP BERT with Celeritas EM standard physics + auto pl = std::make_unique(opts); + run_manager->SetUserInitialization(pl.release()); } - case PhysicsListSelection::geant_physics_list: { - auto opts = setup.GetPhysicsOptions(); - if (std::find( - ignore_processes.begin(), ignore_processes.end(), "Rayl") - != ignore_processes.end()) - { - opts.rayleigh_scattering = false; - } - run_manager->SetUserInitialization( - new detail::GeantPhysicsList{opts}); - break; + else + { + // Celeritas EM standard physics only + auto pl = std::make_unique(opts); + run_manager->SetUserInitialization(pl.release()); } - default: - CELER_ASSERT_UNREACHABLE(); } // Create action initializer diff --git a/app/celer-g4/test-harness.py b/app/celer-g4/test-harness.py index eb57f6feac..16e80a6524 100755 --- a/app/celer-g4/test-harness.py +++ b/app/celer-g4/test-harness.py @@ -73,7 +73,7 @@ def strtobool(text): "num_track_slots": max_tracks, "initializer_capacity": init_capacity, "secondary_stack_factor": 2, - "physics_list": "ftfp_bert", + "physics_list": "celer_ftfp_bert", "field_type": "uniform", "field": [ 0.0, 0.0, 1.0 ], "field_options": { diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 509743bc3f..0caf13e741 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -114,11 +114,14 @@ if(CELERITAS_USE_Geant4) ext/GeantImporter.cc ext/GeantSetup.cc ext/GeantVolumeMapper.cc + ext/detail/CelerEmPhysicsList.cc + ext/detail/CelerEmStandardPhysics.cc + ext/detail/CelerFTFPBert.cc ext/detail/GeantBremsstrahlungProcess.cc ext/detail/GeantMicroXsCalculator.cc ext/detail/GeantModelImporter.cc - ext/detail/GeantPhysicsList.cc ext/detail/GeantProcessImporter.cc + ext/detail/MuHadEmStandardPhysics.cc ) celeritas_get_g4libs(_cg4_libs global geometry materials processes run physicslists tasking) diff --git a/src/celeritas/ext/GeantSetup.cc b/src/celeritas/ext/GeantSetup.cc index 68fc2b7a80..b3adc9924e 100644 --- a/src/celeritas/ext/GeantSetup.cc +++ b/src/celeritas/ext/GeantSetup.cc @@ -31,7 +31,7 @@ #include "geocel/ScopedGeantExceptionHandler.hh" #include "geocel/ScopedGeantLogger.hh" -#include "detail/GeantPhysicsList.hh" +#include "detail/CelerEmPhysicsList.hh" namespace celeritas { @@ -114,7 +114,8 @@ GeantSetup::GeantSetup(std::string const& gdml_filename, Options options) run_manager_->SetUserInitialization(detector.release()); // Construct the physics - auto physics_list = std::make_unique(options); + auto physics_list + = std::make_unique(options); run_manager_->SetUserInitialization(physics_list.release()); } diff --git a/src/celeritas/ext/detail/CelerEmPhysicsList.cc b/src/celeritas/ext/detail/CelerEmPhysicsList.cc new file mode 100644 index 0000000000..951bfaf972 --- /dev/null +++ b/src/celeritas/ext/detail/CelerEmPhysicsList.cc @@ -0,0 +1,39 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/detail/CelerEmPhysicsList.cc +//---------------------------------------------------------------------------// +#include "CelerEmPhysicsList.hh" + +#include + +#include "celeritas/Quantities.hh" + +#include "CelerEmStandardPhysics.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with physics options. + */ +CelerEmPhysicsList::CelerEmPhysicsList(Options const& options) +{ + using ClhepLen = Quantity; + + this->SetVerboseLevel(options.verbose); + this->SetDefaultCutValue( + native_value_to(options.default_cutoff).value()); + + // Celeritas-supported EM Physics + auto em_standard = std::make_unique(options); + RegisterPhysics(em_standard.release()); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/ext/detail/CelerEmPhysicsList.hh b/src/celeritas/ext/detail/CelerEmPhysicsList.hh new file mode 100644 index 0000000000..04b0c04cd5 --- /dev/null +++ b/src/celeritas/ext/detail/CelerEmPhysicsList.hh @@ -0,0 +1,37 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2020-2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/detail/CelerEmPhysicsList.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "../GeantPhysicsOptions.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct a user-defined physics list of particles and physics processes. + */ +class CelerEmPhysicsList : public G4VModularPhysicsList +{ + public: + //!@{ + //! \name Type aliases + using Options = GeantPhysicsOptions; + //!@} + + public: + // Set up during construction + explicit CelerEmPhysicsList(Options const& options); +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/ext/detail/GeantPhysicsList.cc b/src/celeritas/ext/detail/CelerEmStandardPhysics.cc similarity index 95% rename from src/celeritas/ext/detail/GeantPhysicsList.cc rename to src/celeritas/ext/detail/CelerEmStandardPhysics.cc index 4da195707a..9bc7eba8aa 100644 --- a/src/celeritas/ext/detail/GeantPhysicsList.cc +++ b/src/celeritas/ext/detail/CelerEmStandardPhysics.cc @@ -3,9 +3,9 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/ext/detail/GeantPhysicsList.cc +//! \file celeritas/ext/detail/CelerEmStandardPhysics.cc //---------------------------------------------------------------------------// -#include "GeantPhysicsList.hh" +#include "CelerEmStandardPhysics.hh" #include #include @@ -73,7 +73,8 @@ from_msc_step_algorithm(MscStepLimitAlgorithm const& msc_step_algorithm) /*! * Construct with physics options. */ -GeantPhysicsList::GeantPhysicsList(Options const& options) : options_(options) +CelerEmStandardPhysics::CelerEmStandardPhysics(Options const& options) + : options_(options) { // Set EM options using limits from G4EmParameters auto& em_parameters = *G4EmParameters::Instance(); @@ -81,8 +82,6 @@ GeantPhysicsList::GeantPhysicsList(Options const& options) : options_(options) << "number of EM bins per decade=" << options.em_bins_per_decade << " (must be at least 5)"); - using ClhepLen = Quantity; - em_parameters.SetNumberOfBinsPerDecade(options.em_bins_per_decade); em_parameters.SetLossFluctuations(options.eloss_fluctuation); em_parameters.SetMinEnergy(value_as(options.min_energy) @@ -98,6 +97,8 @@ GeantPhysicsList::GeantPhysicsList(Options const& options) : options_(options) from_msc_step_algorithm(options.msc_step_algorithm)); em_parameters.SetMscRangeFactor(options.msc_range_factor); #if G4VERSION_NUMBER >= 1060 + using ClhepLen = Quantity; + // Customizable MSC safety factor/lambda limit were added in // emutils-V10-05-18 em_parameters.SetMscSafetyFactor(options.msc_safety_factor); @@ -108,12 +109,7 @@ GeantPhysicsList::GeantPhysicsList(Options const& options) : options_(options) value_as(options.lowest_electron_energy) * CLHEP::MeV); em_parameters.SetApplyCuts(options.apply_cuts); - this->SetDefaultCutValue( - native_value_to(options.default_cutoff).value()); - - int verb = options_.verbose ? 1 : 0; - this->SetVerboseLevel(verb); - em_parameters.SetVerbose(verb); + em_parameters.SetVerbose(options.verbose); } //---------------------------------------------------------------------------// @@ -129,7 +125,7 @@ GeantPhysicsList::GeantPhysicsList(Options const& options) : options_(options) * Currently only instantiating e+, e-, gamma, and proton (the latter is needed * for msc) */ -void GeantPhysicsList::ConstructParticle() +void CelerEmStandardPhysics::ConstructParticle() { G4Gamma::GammaDefinition(); G4Electron::ElectronDefinition(); @@ -144,11 +140,8 @@ void GeantPhysicsList::ConstructParticle() /*! * Build list of available processes and models. */ -void GeantPhysicsList::ConstructProcess() +void CelerEmStandardPhysics::ConstructProcess() { - // Applies to all constructed particles - G4VUserPhysicsList::AddTransportation(); - // Add E.M. processes for photons, electrons, and positrons this->add_gamma_processes(); this->add_e_processes(G4Electron::Electron()); @@ -173,7 +166,7 @@ void GeantPhysicsList::ConstructProcess() * calculates a combined total cross section. It's faster in Geant4 but * shouldn't result in different answers. */ -void GeantPhysicsList::add_gamma_processes() +void CelerEmStandardPhysics::add_gamma_processes() { auto* physics_list = G4PhysicsListHelper::GetPhysicsListHelper(); auto* gamma = G4Gamma::Gamma(); @@ -267,7 +260,7 @@ void GeantPhysicsList::add_gamma_processes() * - Coulomb scattering and multiple scattering (high E) are currently * disabled. */ -void GeantPhysicsList::add_e_processes(G4ParticleDefinition* p) +void CelerEmStandardPhysics::add_e_processes(G4ParticleDefinition* p) { auto* physics_list = G4PhysicsListHelper::GetPhysicsListHelper(); diff --git a/src/celeritas/ext/detail/GeantPhysicsList.hh b/src/celeritas/ext/detail/CelerEmStandardPhysics.hh similarity index 82% rename from src/celeritas/ext/detail/GeantPhysicsList.hh rename to src/celeritas/ext/detail/CelerEmStandardPhysics.hh index aae256d6cc..2262f82c1a 100644 --- a/src/celeritas/ext/detail/GeantPhysicsList.hh +++ b/src/celeritas/ext/detail/CelerEmStandardPhysics.hh @@ -3,12 +3,12 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/ext/detail/GeantPhysicsList.hh +//! \file celeritas/ext/detail/CelerEmStandardPhysics.hh //---------------------------------------------------------------------------// #pragma once #include -#include +#include #include "../GeantPhysicsOptions.hh" @@ -18,9 +18,9 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Construct a user-defined physics list of particles and physics processes. + * Construct Celeritas-supported EM standard physics. */ -class GeantPhysicsList : public G4VUserPhysicsList +class CelerEmStandardPhysics : public G4VPhysicsConstructor { public: //!@{ @@ -30,7 +30,7 @@ class GeantPhysicsList : public G4VUserPhysicsList public: // Set up during construction - explicit GeantPhysicsList(Options const& options); + explicit CelerEmStandardPhysics(Options const& options); // Set up minimal EM particle list void ConstructParticle() override; diff --git a/src/celeritas/ext/detail/CelerFTFPBert.cc b/src/celeritas/ext/detail/CelerFTFPBert.cc new file mode 100644 index 0000000000..73f5451cf1 --- /dev/null +++ b/src/celeritas/ext/detail/CelerFTFPBert.cc @@ -0,0 +1,82 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/detail/CelerFTFPBert.cc +//---------------------------------------------------------------------------// +#include "CelerFTFPBert.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "celeritas/Quantities.hh" + +#include "CelerEmStandardPhysics.hh" +#include "MuHadEmStandardPhysics.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct the FTFP BERT physics list with modified EM standard physics. + */ +CelerFTFPBert::CelerFTFPBert(Options const& options) +{ + using ClhepLen = Quantity; + + int verbosity = options.verbose; + this->SetVerboseLevel(verbosity); + this->SetDefaultCutValue( + native_value_to(options.default_cutoff).value()); + + // Celeritas-supported EM physics + auto celer_em = std::make_unique(options); + RegisterPhysics(celer_em.release()); + + // Muon and hadrom EM standard physics not supported in Celeritas + auto muhad_em = std::make_unique(verbosity); + RegisterPhysics(muhad_em.release()); + + // Synchroton radiation & GN physics + auto em_extra = std::make_unique(verbosity); + RegisterPhysics(em_extra.release()); + + // Decays + auto decay = std::make_unique(verbosity); + RegisterPhysics(decay.release()); + + // Hadron elastic scattering + auto hadron_elastic = std::make_unique(verbosity); + RegisterPhysics(hadron_elastic.release()); + + // Hadron physics + auto hadron = std::make_unique(verbosity); + RegisterPhysics(hadron.release()); + + // Stopping physics + auto stopping = std::make_unique(verbosity); + RegisterPhysics(stopping.release()); + + // Ion physics + auto ion = std::make_unique(verbosity); + RegisterPhysics(ion.release()); + + // Neutron tracking cut + auto neutron_cut = std::make_unique(verbosity); + RegisterPhysics(neutron_cut.release()); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/ext/detail/CelerFTFPBert.hh b/src/celeritas/ext/detail/CelerFTFPBert.hh new file mode 100644 index 0000000000..4c5fa6bacd --- /dev/null +++ b/src/celeritas/ext/detail/CelerFTFPBert.hh @@ -0,0 +1,37 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/detail/CelerFTFPBert.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "../GeantPhysicsOptions.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct the FTFP_BERT physics list with modified EM standard physics. + */ +class CelerFTFPBert : public G4VModularPhysicsList +{ + public: + //!@{ + //! \name Type aliases + using Options = GeantPhysicsOptions; + //!@} + + public: + // Construct with physics options + explicit CelerFTFPBert(Options const& options); +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/ext/detail/MuHadEmStandardPhysics.cc b/src/celeritas/ext/detail/MuHadEmStandardPhysics.cc new file mode 100644 index 0000000000..80781fd8e1 --- /dev/null +++ b/src/celeritas/ext/detail/MuHadEmStandardPhysics.cc @@ -0,0 +1,272 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/detail/MuHadEmStandardPhysics.cc +//---------------------------------------------------------------------------// +#include "MuHadEmStandardPhysics.hh" + +#include +#include +#include +#include +#include +#include +#include +#if G4VERSION_NUMBER >= 1070 +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include "G4GenericIon.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with verbosity. + */ +MuHadEmStandardPhysics::MuHadEmStandardPhysics(int verbosity) +{ + G4EmParameters::Instance()->SetVerbose(verbosity); +} + +//---------------------------------------------------------------------------// +/*! + * Build list of particles. + */ +void MuHadEmStandardPhysics::ConstructParticle() +{ + this->construct_particle(); +} + +//---------------------------------------------------------------------------// +/*! + * Build processes and models. + */ +void MuHadEmStandardPhysics::ConstructProcess() +{ + this->construct_process(); +} + +//---------------------------------------------------------------------------// +/*! + * Set up minimal EM particle list. + * + * This is required to support Geant4 versions less than 10.7.0 which do not + * have the G4EmBuilder. + */ +void MuHadEmStandardPhysics::construct_particle() +{ +#if G4VERSION_NUMBER >= 1070 + G4EmBuilder::ConstructMinimalEmSet(); +#else + // Pseudo-particles + G4Geantino::GeantinoDefinition(); + G4ChargedGeantino::ChargedGeantinoDefinition(); + G4NeutrinoMu::NeutrinoMu(); + G4AntiNeutrinoMu::AntiNeutrinoMu(); + G4NeutrinoE::NeutrinoE(); + G4AntiNeutrinoE::AntiNeutrinoE(); + + // Leptons + G4MuonPlus::MuonPlus(); + G4MuonMinus::MuonMinus(); + + // Mesons + G4PionPlus::PionPlus(); + G4PionMinus::PionMinus(); + G4PionZero::PionZero(); + G4KaonPlus::KaonPlus(); + G4KaonMinus::KaonMinus(); + + // Barions + G4Proton::Proton(); + G4AntiProton::AntiProton(); + G4Neutron::Neutron(); + G4AntiNeutron::AntiNeutron(); + G4Lambda::Lambda(); + G4AntiLambda::AntiLambda(); + + // Ions + G4Deuteron::Deuteron(); + G4Triton::Triton(); + G4He3::He3(); + G4Alpha::Alpha(); + G4GenericIon::GenericIon(); +#endif +} + +//---------------------------------------------------------------------------// +/*! + * Build processes and models. + * + * This is required to support Geant4 versions less than 10.7.0 which do not + * have the G4EmBuilder. This constructs the muon and hadron physics as in the + * \c G4EmStandardPhysics::ConstructProcess() method of Geant4 version 10.6.0. + */ +void MuHadEmStandardPhysics::construct_process() +{ + auto* ph = G4PhysicsListHelper::GetPhysicsListHelper(); +#if G4VERSION_NUMBER >= 1070 + G4ParticleDefinition* particle = G4GenericIon::GenericIon(); + + auto niel_energy_limit = G4EmParameters::Instance()->MaxNIELEnergy(); + G4NuclearStopping* nuclear_stopping = nullptr; + if (niel_energy_limit > 0) + { + // Nuclear stopping is enabled if the energy limit is above zero + nuclear_stopping = new G4NuclearStopping(); + nuclear_stopping->SetMaxKinEnergy(niel_energy_limit); + } + G4hMultipleScattering* ion_msc = new G4hMultipleScattering("ionmsc"); + G4ionIonisation* ion_ionization = new G4ionIonisation(); + + ph->RegisterProcess(ion_msc, particle); + ph->RegisterProcess(ion_ionization, particle); + if (nuclear_stopping) + { + ph->RegisterProcess(nuclear_stopping, particle); + } + + G4EmBuilder::ConstructCharged(ion_msc, nuclear_stopping); +#else + // Muon and hadron bremsstrahlung and pair production + G4MuBremsstrahlung* mu_brems = new G4MuBremsstrahlung(); + G4MuPairProduction* mu_pair = new G4MuPairProduction(); + G4hBremsstrahlung* pi_brems = new G4hBremsstrahlung(); + G4hPairProduction* pi_pair = new G4hPairProduction(); + G4hBremsstrahlung* ka_brems = new G4hBremsstrahlung(); + G4hPairProduction* ka_pair = new G4hPairProduction(); + G4hBremsstrahlung* prot_brems = new G4hBremsstrahlung(); + G4hPairProduction* prot_pair = new G4hPairProduction(); + + // Muon & hadron multiple scattering + G4MuMultipleScattering* mu_msc = new G4MuMultipleScattering(); + mu_msc->SetEmModel(new G4WentzelVIModel()); + G4CoulombScattering* mu_coulomb = new G4CoulombScattering(); + + G4hMultipleScattering* pi_msc = new G4hMultipleScattering(); + pi_msc->SetEmModel(new G4WentzelVIModel()); + G4CoulombScattering* pi_coulomb = new G4CoulombScattering(); + + G4hMultipleScattering* ka_msc = new G4hMultipleScattering(); + ka_msc->SetEmModel(new G4WentzelVIModel()); + G4CoulombScattering* ka_coulomb = new G4CoulombScattering(); + + G4hMultipleScattering* ion_msc = new G4hMultipleScattering("ionmsc"); + + // Add standard muon and hadron EM Processes + G4EmParticleList particle_list; + G4ParticleTable* table = G4ParticleTable::GetParticleTable(); + for (auto const& name : particle_list.PartNames()) + { + G4ParticleDefinition* particle = table->FindParticle(name); + if (!particle) + { + continue; + } + else if (name == "mu+" || name == "mu-") + { + ph->RegisterProcess(mu_msc, particle); + ph->RegisterProcess(new G4MuIonisation(), particle); + ph->RegisterProcess(mu_brems, particle); + ph->RegisterProcess(mu_pair, particle); + ph->RegisterProcess(mu_coulomb, particle); + } + else if (name == "alpha" || name == "He3") + { + ph->RegisterProcess(new G4hMultipleScattering(), particle); + ph->RegisterProcess(new G4ionIonisation(), particle); + } + else if (name == "GenericIon") + { + ph->RegisterProcess(ion_msc, particle); + ph->RegisterProcess(new G4ionIonisation(), particle); + } + else if (name == "pi+" || name == "pi-") + { + ph->RegisterProcess(pi_msc, particle); + ph->RegisterProcess(new G4hIonisation(), particle); + ph->RegisterProcess(pi_brems, particle); + ph->RegisterProcess(pi_pair, particle); + ph->RegisterProcess(pi_coulomb, particle); + } + else if (name == "kaon+" || name == "kaon-") + { + ph->RegisterProcess(ka_msc, particle); + ph->RegisterProcess(new G4hIonisation(), particle); + ph->RegisterProcess(ka_brems, particle); + ph->RegisterProcess(ka_pair, particle); + ph->RegisterProcess(ka_coulomb, particle); + } + else if (name == "proton" || name == "anti_proton") + { + G4hMultipleScattering* prot_msc = new G4hMultipleScattering(); + prot_msc->SetEmModel(new G4WentzelVIModel()); + + ph->RegisterProcess(prot_msc, particle); + ph->RegisterProcess(new G4hIonisation(), particle); + ph->RegisterProcess(prot_brems, particle); + ph->RegisterProcess(prot_pair, particle); + ph->RegisterProcess(new G4CoulombScattering(), particle); + } + else if (name == "B+" || name == "B-" || name == "D+" || name == "D-" + || name == "Ds+" || name == "Ds-" || name == "anti_He3" + || name == "anti_alpha" || name == "anti_deuteron" + || name == "anti_lambda_c+" || name == "anti_omega-" + || name == "anti_sigma_c+" || name == "anti_sigma_c++" + || name == "anti_sigma+" || name == "anti_sigma-" + || name == "anti_triton" || name == "anti_xi_c+" + || name == "anti_xi-" || name == "deuteron" + || name == "lambda_c+" || name == "omega-" + || name == "sigma_c+" || name == "sigma_c++" + || name == "sigma+" || name == "sigma-" || name == "tau+" + || name == "tau-" || name == "triton" || name == "xi_c+" + || name == "xi-") + { + ph->RegisterProcess(ion_msc, particle); + ph->RegisterProcess(new G4hIonisation(), particle); + } + } +#endif +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/ext/detail/MuHadEmStandardPhysics.hh b/src/celeritas/ext/detail/MuHadEmStandardPhysics.hh new file mode 100644 index 0000000000..e9b720baf6 --- /dev/null +++ b/src/celeritas/ext/detail/MuHadEmStandardPhysics.hh @@ -0,0 +1,38 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/detail/MuHadEmStandardPhysics.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct EM standard physics not implemented in Celeritas. + */ +class MuHadEmStandardPhysics : public G4VPhysicsConstructor +{ + public: + // Set up during construction + explicit MuHadEmStandardPhysics(int verbosity); + + // Set up minimal EM particle list + void ConstructParticle() override; + // Set up process list + void ConstructProcess() override; + + private: + void construct_particle(); + void construct_process(); +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas From 4a87e39d1e3031124f5ec190a2bbf7674933042f Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Mon, 20 May 2024 14:52:01 +0100 Subject: [PATCH 53/59] Add external bounding box to generic trapezoid (#1244) * Add exterior bounding box for gentrap * Add bounding box grower * Just use fmin/fmax instead of algorithm min/max * Add 'grow' function for convex region * Address @mrguilima feedback from #1237 * Updated unit test for new boundaries --- src/corecel/math/Algorithms.hh | 24 ++--- src/geocel/BoundingBox.hh | 28 +++++- src/orange/orangeinp/ConvexRegion.cc | 16 +++ src/orange/surf/SurfaceSimplifier.hh | 2 +- .../data/inputbuilder-incomplete-bb.org.json | 8 +- test/orange/orangeinp/ConvexRegion.test.cc | 99 +++++++++---------- test/orange/orangeinp/UnitProto.test.cc | 3 +- 7 files changed, 101 insertions(+), 79 deletions(-) diff --git a/src/corecel/math/Algorithms.hh b/src/corecel/math/Algorithms.hh index 4ece3ff22b..cb23377bc3 100644 --- a/src/corecel/math/Algorithms.hh +++ b/src/corecel/math/Algorithms.hh @@ -329,23 +329,17 @@ CELER_FORCEINLINE_FUNCTION void sort(RandomAccessIt first, RandomAccessIt last) * This function is specialized when building CUDA device code, which has * special intrinsics for max. */ -#ifndef __CUDA_ARCH__ -template -#else -template::value, bool> = true> -#endif +template::value, bool> = true> CELER_CONSTEXPR_FUNCTION T const& max(T const& a, T const& b) noexcept { return (b > a) ? b : a; } -#ifdef __CUDA_ARCH__ -template::value, bool> = true> +template::value, bool> = true> CELER_CONSTEXPR_FUNCTION T max(T a, T b) noexcept { - return ::max(a, b); + return std::fmax(a, b); } -#endif //---------------------------------------------------------------------------// /*! @@ -354,23 +348,17 @@ CELER_CONSTEXPR_FUNCTION T max(T a, T b) noexcept * This function is specialized when building CUDA device code, which has * special intrinsics for min. */ -#ifndef __CUDA_ARCH__ -template -#else -template::value, bool> = true> -#endif +template::value, bool> = true> CELER_CONSTEXPR_FUNCTION T const& min(T const& a, T const& b) noexcept { return (b < a) ? b : a; } -#ifdef __CUDA_ARCH__ -template::value, bool> = true> +template::value, bool> = true> CELER_CONSTEXPR_FUNCTION T min(T a, T b) noexcept { - return ::min(a, b); + return std::fmin(a, b); } -#endif //---------------------------------------------------------------------------// /*! diff --git a/src/geocel/BoundingBox.hh b/src/geocel/BoundingBox.hh index 5caf294135..6d6a5f5d6d 100644 --- a/src/geocel/BoundingBox.hh +++ b/src/geocel/BoundingBox.hh @@ -7,13 +7,13 @@ //---------------------------------------------------------------------------// #pragma once +#include #include #include "corecel/Assert.hh" #include "corecel/Macros.hh" #include "corecel/Types.hh" #include "corecel/cont/Array.hh" -#include "corecel/math/Algorithms.hh" #include "corecel/math/NumericLimits.hh" #include "Types.hh" @@ -95,6 +95,9 @@ class BoundingBox CELER_CONSTEXPR_FUNCTION void grow(Bound bnd, Axis axis, real_type position); + // Increase the bounding box's extent on both bounds + CELER_CONSTEXPR_FUNCTION void grow(Axis axis, real_type position); + private: Array points_; //!< lo/hi points @@ -257,11 +260,11 @@ BoundingBox::shrink(Bound bnd, Axis axis, real_type position) real_type p = points_[to_int(bnd)][to_int(axis)]; if (bnd == Bound::lo) { - p = ::celeritas::max(p, position); + p = std::fmax(p, position); } else { - p = ::celeritas::min(p, position); + p = std::fmin(p, position); } points_[to_int(bnd)][to_int(axis)] = p; } @@ -280,14 +283,29 @@ BoundingBox::grow(Bound bnd, Axis axis, real_type position) real_type p = points_[to_int(bnd)][to_int(axis)]; if (bnd == Bound::lo) { - p = ::celeritas::min(p, position); + p = std::fmin(p, position); } else { - p = ::celeritas::max(p, position); + p = std::fmax(p, position); } points_[to_int(bnd)][to_int(axis)] = p; } +//---------------------------------------------------------------------------// +/*! + * Increase (expand) the bounding box's extent along an axis. + * + * If the point is outside the box, the box is expanded so the given boundary + * is on that point. Otherwise no change is made. + */ +template +CELER_CONSTEXPR_FUNCTION void +BoundingBox::grow(Axis axis, real_type position) +{ + this->grow(Bound::lo, axis, position); + this->grow(Bound::hi, axis, position); +} + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/ConvexRegion.cc index 7cda1cf08a..e49fa0fb0d 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/ConvexRegion.cc @@ -487,6 +487,22 @@ void GenTrap::build(ConvexSurfaceBuilder& insert_surface) const GeneralQuadric{abc, def, ghi, offset}); } } + + // Construct exterior bounding box + BBox exterior_bbox; + for (VecReal2 const* p : {&lo_, &hi_}) + { + for (Real2 const& xy : *p) + { + for (auto ax : {Axis::x, Axis::y}) + { + exterior_bbox.grow(ax, xy[to_int(ax)]); + } + } + } + exterior_bbox.grow(Bound::lo, Axis::z, -hz_); + exterior_bbox.grow(Bound::hi, Axis::z, hz_); + insert_surface(Sense::inside, exterior_bbox); } //---------------------------------------------------------------------------// diff --git a/src/orange/surf/SurfaceSimplifier.hh b/src/orange/surf/SurfaceSimplifier.hh index 7de6be915b..7e7faaad05 100644 --- a/src/orange/surf/SurfaceSimplifier.hh +++ b/src/orange/surf/SurfaceSimplifier.hh @@ -82,7 +82,7 @@ class SurfaceSimplifier // Quadric can be normalized or simplified Optional operator()(GeneralQuadric const&); - //! Default: no simplifcation + //! Default: no simplification template std::variant operator()(S const&) const { diff --git a/test/orange/data/inputbuilder-incomplete-bb.org.json b/test/orange/data/inputbuilder-incomplete-bb.org.json index 0f73fcaadc..84166d97d2 100644 --- a/test/orange/data/inputbuilder-incomplete-bb.org.json +++ b/test/orange/data/inputbuilder-incomplete-bb.org.json @@ -210,13 +210,13 @@ { "bbox": [ [ --5.000050000000001, --5.000050000000001, +-2.0, +-2.0, -3.0 ], [ -5.000050000000001, -5.000050000000001, +2.0, +2.0, 3.0 ] ], diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/ConvexRegion.test.cc index 82458e99fe..f3851bc0e8 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/ConvexRegion.test.cc @@ -476,8 +476,8 @@ TEST_F(GenTrapTest, trd1) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -3}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-2, -2, -3}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2, 2, 3}), result.exterior.upper()); } TEST_F(GenTrapTest, trd2) @@ -496,8 +496,8 @@ TEST_F(GenTrapTest, trd2) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -3}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-2, -2, -3}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2, 2, 3}), result.exterior.upper()); } TEST_F(GenTrapTest, ppiped) @@ -518,8 +518,8 @@ TEST_F(GenTrapTest, ppiped) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -4}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 4}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-2, -2, -4}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2, 2, 4}), result.exterior.upper()); } TEST_F(GenTrapTest, triang_prism) @@ -539,8 +539,8 @@ TEST_F(GenTrapTest, triang_prism) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-1, -inf, -3}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 3}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-1, -1, -3}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2, 1, 3}), result.exterior.upper()); } TEST_F(GenTrapTest, trap_corners) @@ -563,8 +563,8 @@ TEST_F(GenTrapTest, trap_corners) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -30, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, 30, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-21, -30, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{21, 30, 40}), result.exterior.upper()); } TEST_F(GenTrapTest, trapezoid_trans) @@ -588,8 +588,8 @@ TEST_F(GenTrapTest, trapezoid_trans) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -60, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, 0, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-51, -60, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{-9, 0, 40}), result.exterior.upper()); } TEST_F(GenTrapTest, trapezoid_ccw) @@ -611,8 +611,8 @@ TEST_F(GenTrapTest, trapezoid_ccw) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -30, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, 30, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-21, -30, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{21, 30, 40}), result.exterior.upper()); } TEST_F(GenTrapTest, trap_theta) @@ -632,8 +632,8 @@ TEST_F(GenTrapTest, trap_theta) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -20, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, 20, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-50, -20, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{50, 20, 40}), result.exterior.upper()); } TEST_F(GenTrapTest, trap_thetaphi) @@ -653,8 +653,8 @@ TEST_F(GenTrapTest, trap_thetaphi) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-10, -inf, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{10, inf, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-10, -60, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{10, 60, 40}), result.exterior.upper()); } TEST_F(GenTrapTest, trap_g4) @@ -679,8 +679,10 @@ TEST_F(GenTrapTest, trap_g4) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -4}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 4}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-1.95920952072934, -2.93923101204883, -4}), + result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2.64848563385739, 3.06076898795117, 4}), + result.exterior.upper()); } TEST_F(GenTrapTest, trap_full) @@ -700,8 +702,10 @@ TEST_F(GenTrapTest, trap_full) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -inf, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-40.2842712474619, -48.2842712474619, -40}), + result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{40.2842712474619, 48.2842712474619, 40}), + result.exterior.upper()); } // TODO: this should be valid @@ -737,8 +741,8 @@ TEST_F(GenTrapTest, full) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -2, -4}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, inf, 4}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-2, -2, -4}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{2, 2, 4}), result.exterior.upper()); } TEST_F(GenTrapTest, full2) @@ -758,33 +762,28 @@ TEST_F(GenTrapTest, full2) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_EQ(expected_surfaces, result.surfaces); EXPECT_FALSE(result.interior) << result.interior; - EXPECT_VEC_SOFT_EQ((Real3{-inf, -20, -40}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, 20, 40}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-52, -20, -40}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{54, 20, 40}), result.exterior.upper()); } +/*! + * Test deduplication of two opposing quadric surfaces. + * + * \verbatim + * Lower polygons: Upper polygons: + * + * x=-1 x=1 x=-0.5 + * +----+----+ y=1 +--+------+ y=1 + * | | | | \ | + * | | R | | \ R | + * | L | | | L \ | + * | | | | \ | + * +----+----+ y=-1 +-------+-+ y=-1 + * x=0 x=0.5 + * \endverbatim + */ TEST_F(GenTrapTest, adjacent_twisted) { - /* Lower polygons: - * - * x=-1 x=1 - * +----+----+ y=1 - * | | | - * | L | R | - * | | | - * | | | - * +----+----+ y=-1 - * - * - * Upper polygons: - * x=-0.5 - * +--+------+ y=1 - * | \ | - * | \ R | - * | L \ | - * | \ | - * +-------+-+ y=-1 - * x=0.5 - */ { // Left auto result @@ -796,7 +795,7 @@ TEST_F(GenTrapTest, adjacent_twisted) EXPECT_EQ(expected_node, result.node); EXPECT_VEC_SOFT_EQ((Real3{-1, -1, -1}), result.exterior.lower()); - EXPECT_VEC_SOFT_EQ((Real3{inf, 1, 1}), result.exterior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{0.5, 1, 1}), result.exterior.upper()); } { // Right @@ -808,7 +807,7 @@ TEST_F(GenTrapTest, adjacent_twisted) static char const expected_node[] = "all(+0, -1, +2, +3, -4, -6)"; EXPECT_EQ(expected_node, result.node); - EXPECT_VEC_SOFT_EQ((Real3{-inf, -1, -1}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{-0.5, -1, -1}), result.exterior.lower()); EXPECT_VEC_SOFT_EQ((Real3{1, 1, 1}), result.exterior.upper()); } { @@ -821,7 +820,7 @@ TEST_F(GenTrapTest, adjacent_twisted) static char const expected_node[] = "all(+0, -1, +7, -8, -9, +10)"; EXPECT_EQ(expected_node, result.node); - EXPECT_VEC_SOFT_EQ((Real3{-inf, -2, -1}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{-1, -2, -1}), result.exterior.lower()); EXPECT_VEC_SOFT_EQ((Real3{2, 2, 1}), result.exterior.upper()); } diff --git a/test/orange/orangeinp/UnitProto.test.cc b/test/orange/orangeinp/UnitProto.test.cc index 27a6e7eba1..9b9ebe4722 100644 --- a/test/orange/orangeinp/UnitProto.test.cc +++ b/test/orange/orangeinp/UnitProto.test.cc @@ -466,7 +466,8 @@ class InputBuilderTest : public UnitProtoTest std::stringstream expected; expected << ref.rdbuf(); - EXPECT_JSON_EQ(expected.str(), actual.str()); + EXPECT_JSON_EQ(expected.str(), actual.str()) + << "update the file at " << ref_path; } }; From f8b23ec9f5051416958fddf937910dd4bde9f80e Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Tue, 21 May 2024 22:12:27 +0100 Subject: [PATCH 54/59] Rename ConvexRegion to IntersectRegion (#1245) * Rename Convex -> Intersect * Update documentation * Fix articles --- src/orange/CMakeLists.txt | 8 +-- .../{ConvexRegion.cc => IntersectRegion.cc} | 24 +++---- .../{ConvexRegion.hh => IntersectRegion.hh} | 62 +++++++++---------- ...eBuilder.cc => IntersectSurfaceBuilder.cc} | 32 +++++----- ...eBuilder.hh => IntersectSurfaceBuilder.hh} | 30 ++++----- src/orange/orangeinp/ObjectIO.json.cc | 6 +- src/orange/orangeinp/ObjectIO.json.hh | 6 +- src/orange/orangeinp/Shape.cc | 4 +- src/orange/orangeinp/Shape.hh | 28 ++++----- src/orange/orangeinp/Solid.cc | 10 +-- src/orange/orangeinp/Solid.hh | 21 ++++--- ...onvexRegion.cc => BuildIntersectRegion.cc} | 24 +++---- ...onvexRegion.hh => BuildIntersectRegion.hh} | 24 +++---- ...rfaceState.cc => IntersectSurfaceState.cc} | 6 +- ...rfaceState.hh => IntersectSurfaceState.hh} | 8 +-- test/orange/CMakeLists.txt | 4 +- test/orange/orangeinp/CsgTestUtils.cc | 4 +- test/orange/orangeinp/CsgTestUtils.hh | 4 +- ...Region.test.cc => IntersectRegion.test.cc} | 57 ++++++++--------- ...est.cc => IntersectSurfaceBuilder.test.cc} | 50 ++++++++------- .../detail/LocalSurfaceInserter.test.cc | 2 +- 21 files changed, 209 insertions(+), 205 deletions(-) rename src/orange/orangeinp/{ConvexRegion.cc => IntersectRegion.cc} (97%) rename src/orange/orangeinp/{ConvexRegion.hh => IntersectRegion.hh} (88%) rename src/orange/orangeinp/{ConvexSurfaceBuilder.cc => IntersectSurfaceBuilder.cc} (85%) rename src/orange/orangeinp/{ConvexSurfaceBuilder.hh => IntersectSurfaceBuilder.hh} (79%) rename src/orange/orangeinp/detail/{BuildConvexRegion.cc => BuildIntersectRegion.cc} (68%) rename src/orange/orangeinp/detail/{BuildConvexRegion.hh => BuildIntersectRegion.hh} (55%) rename src/orange/orangeinp/detail/{ConvexSurfaceState.cc => IntersectSurfaceState.cc} (90%) rename src/orange/orangeinp/detail/{ConvexSurfaceState.hh => IntersectSurfaceState.hh} (89%) rename test/orange/orangeinp/{ConvexRegion.test.cc => IntersectRegion.test.cc} (96%) rename test/orange/orangeinp/{ConvexSurfaceBuilder.test.cc => IntersectSurfaceBuilder.test.cc} (87%) diff --git a/src/orange/CMakeLists.txt b/src/orange/CMakeLists.txt index a0ae520518..f05e29303f 100644 --- a/src/orange/CMakeLists.txt +++ b/src/orange/CMakeLists.txt @@ -25,23 +25,23 @@ list(APPEND SOURCES detail/SurfacesRecordBuilder.cc detail/UnitInserter.cc detail/UniverseInserter.cc - orangeinp/ConvexRegion.cc - orangeinp/ConvexSurfaceBuilder.cc orangeinp/CsgObject.cc orangeinp/CsgTree.cc orangeinp/CsgTypes.cc orangeinp/CsgTreeUtils.cc + orangeinp/IntersectRegion.cc + orangeinp/IntersectSurfaceBuilder.cc orangeinp/ProtoInterface.cc orangeinp/Shape.cc orangeinp/Solid.cc orangeinp/Transformed.cc orangeinp/UnitProto.cc orangeinp/detail/BoundingZone.cc - orangeinp/detail/BuildConvexRegion.cc - orangeinp/detail/ConvexSurfaceState.cc + orangeinp/detail/BuildIntersectRegion.cc orangeinp/detail/CsgUnitBuilder.cc orangeinp/detail/InputBuilder.cc orangeinp/detail/InternalSurfaceFlagger.cc + orangeinp/detail/IntersectSurfaceState.cc orangeinp/detail/LocalSurfaceInserter.cc orangeinp/detail/NodeSimplifier.cc orangeinp/detail/PostfixLogicBuilder.cc diff --git a/src/orange/orangeinp/ConvexRegion.cc b/src/orange/orangeinp/IntersectRegion.cc similarity index 97% rename from src/orange/orangeinp/ConvexRegion.cc rename to src/orange/orangeinp/IntersectRegion.cc index e49fa0fb0d..f86335e0a9 100644 --- a/src/orange/orangeinp/ConvexRegion.cc +++ b/src/orange/orangeinp/IntersectRegion.cc @@ -3,9 +3,9 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/ConvexRegion.cc +//! \file orange/orangeinp/IntersectRegion.cc //---------------------------------------------------------------------------// -#include "ConvexRegion.hh" +#include "IntersectRegion.hh" #include @@ -23,7 +23,7 @@ #include "orange/surf/SimpleQuadric.hh" #include "orange/surf/SphereCentered.hh" -#include "ConvexSurfaceBuilder.hh" +#include "IntersectSurfaceBuilder.hh" #if CELERITAS_USE_JSON # include "ObjectIO.json.hh" @@ -69,7 +69,7 @@ Box::Box(Real3 const& halfwidths) : hw_{halfwidths} /*! * Build surfaces. */ -void Box::build(ConvexSurfaceBuilder& insert_surface) const +void Box::build(IntersectSurfaceBuilder& insert_surface) const { constexpr auto X = to_int(Axis::x); constexpr auto Y = to_int(Axis::y); @@ -132,7 +132,7 @@ bool Cone::encloses(Cone const& other) const * - Truncate z so that it's not outside of the half-height * - Project that radial slice onto the xz plane by multiplying 1/sqrt(2) */ -void Cone::build(ConvexSurfaceBuilder& insert_surface) const +void Cone::build(IntersectSurfaceBuilder& insert_surface) const { // Build the bottom and top planes insert_surface(Sense::outside, PlaneZ{-hh_}); @@ -229,7 +229,7 @@ bool Cylinder::encloses(Cylinder const& other) const /*! * Build surfaces. */ -void Cylinder::build(ConvexSurfaceBuilder& insert_surface) const +void Cylinder::build(IntersectSurfaceBuilder& insert_surface) const { insert_surface(Sense::outside, PlaneZ{-hh_}); insert_surface(Sense::inside, PlaneZ{hh_}); @@ -265,7 +265,7 @@ Ellipsoid::Ellipsoid(Real3 const& radii) : radii_{radii} /*! * Build surfaces. */ -void Ellipsoid::build(ConvexSurfaceBuilder& insert_surface) const +void Ellipsoid::build(IntersectSurfaceBuilder& insert_surface) const { // Second-order coefficients are product of the other two squared radii; // Zeroth-order coefficient is the product of all three squared radii @@ -435,7 +435,7 @@ GenTrap::GenTrap(real_type halfz, VecReal2 const& lo, VecReal2 const& hi) /*! * Build surfaces. */ -void GenTrap::build(ConvexSurfaceBuilder& insert_surface) const +void GenTrap::build(IntersectSurfaceBuilder& insert_surface) const { // Build the bottom and top planes insert_surface(Sense::outside, PlaneZ{-hz_}); @@ -538,7 +538,7 @@ InfWedge::InfWedge(Turn start, Turn interior) * Both planes should point "outward" to the wedge. In the degenerate case of * interior = 0.5 we rely on CSG object deduplication. */ -void InfWedge::build(ConvexSurfaceBuilder& insert_surface) const +void InfWedge::build(IntersectSurfaceBuilder& insert_surface) const { real_type sinstart, cosstart, sinend, cosend; sincos(start_, &sinstart, &cosstart); @@ -593,7 +593,7 @@ Parallelepiped::Parallelepiped(Real3 const& half_projs, /*! * Build surfaces. */ -void Parallelepiped::build(ConvexSurfaceBuilder& insert_surface) const +void Parallelepiped::build(IntersectSurfaceBuilder& insert_surface) const { constexpr auto X = to_int(Axis::x); constexpr auto Y = to_int(Axis::y); @@ -669,7 +669,7 @@ Prism::Prism(int num_sides, /*! * Build surfaces. */ -void Prism::build(ConvexSurfaceBuilder& insert_surface) const +void Prism::build(IntersectSurfaceBuilder& insert_surface) const { using constants::pi; @@ -736,7 +736,7 @@ Sphere::Sphere(real_type radius) : radius_{radius} /*! * Build surfaces. */ -void Sphere::build(ConvexSurfaceBuilder& insert_surface) const +void Sphere::build(IntersectSurfaceBuilder& insert_surface) const { insert_surface(SphereCentered{radius_}); } diff --git a/src/orange/orangeinp/ConvexRegion.hh b/src/orange/orangeinp/IntersectRegion.hh similarity index 88% rename from src/orange/orangeinp/ConvexRegion.hh rename to src/orange/orangeinp/IntersectRegion.hh index 245bcbb14e..9acb6b3ff1 100644 --- a/src/orange/orangeinp/ConvexRegion.hh +++ b/src/orange/orangeinp/IntersectRegion.hh @@ -3,8 +3,8 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/ConvexRegion.hh -//! \brief Contains ConvexRegionInterface and concrete daughters +//! \file orange/orangeinp/IntersectRegion.hh +//! \brief Contains IntersectRegionInterface and concrete daughters //---------------------------------------------------------------------------// #pragma once @@ -19,19 +19,20 @@ struct JsonPimpl; namespace orangeinp { -class ConvexSurfaceBuilder; +class IntersectSurfaceBuilder; //---------------------------------------------------------------------------// /*! * Interface class for building non-reentrant spatial regions. * * This is a building block for constructing more complex objects out of - * smaller spatial regions. A \c shape object will have a single convex region, - * and a \c solid object region may have multiple adjacent convex regions. + * smaller spatial regions. A \c shape object will have a single intersect + * region, and a \c solid object region may have multiple adjacent intersect + * regions. * * Convex regions should be as minimal as possible and rely on transformations * to change axes, displacement, etc. As a general rule, the exterior bounding - * box of a convex region should be centered on the origin, and + * box of a intersect region should be centered on the origin, and * objects should be aligned along the \em z axis. * * When implementing this class, prefer to build simpler surfaces (planes) @@ -40,11 +41,11 @@ class ConvexSurfaceBuilder; * * \note Additional methods such as volume calculation may be added here later. */ -class ConvexRegionInterface +class IntersectRegionInterface { public: //! Construct surfaces that are AND-ed into this region - virtual void build(ConvexSurfaceBuilder&) const = 0; + virtual void build(IntersectSurfaceBuilder&) const = 0; //! Write the region to a JSON object virtual void output(JsonPimpl*) const = 0; @@ -52,9 +53,9 @@ class ConvexRegionInterface protected: //!@{ //! Allow construction and assignment only through daughter classes - ConvexRegionInterface() = default; - virtual ~ConvexRegionInterface() = default; - CELER_DEFAULT_COPY_MOVE(ConvexRegionInterface); + IntersectRegionInterface() = default; + virtual ~IntersectRegionInterface() = default; + CELER_DEFAULT_COPY_MOVE(IntersectRegionInterface); //!@} }; @@ -62,14 +63,14 @@ class ConvexRegionInterface /*! * A rectangular parallelepiped/cuboid centered on the origin. */ -class Box final : public ConvexRegionInterface +class Box final : public IntersectRegionInterface { public: // Construct with half-widths explicit Box(Real3 const& halfwidths); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -96,10 +97,10 @@ class Box final : public ConvexRegionInterface * have a single radius of zero, which puts the vanishing point on one end of * the cone. * - * This convex region, along with the Cylinder, is a base component of the + * This intersect region, along with the Cylinder, is a base component of the * G4Polycone (PCON). */ -class Cone final : public ConvexRegionInterface +class Cone final : public IntersectRegionInterface { public: //!@{ @@ -114,7 +115,7 @@ class Cone final : public ConvexRegionInterface //// INTERFACE //// // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -141,14 +142,14 @@ class Cone final : public ConvexRegionInterface /*! * A Z-aligned cylinder centered on the origin. */ -class Cylinder final : public ConvexRegionInterface +class Cylinder final : public IntersectRegionInterface { public: // Construct with radius Cylinder(real_type radius, real_type halfheight); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -175,14 +176,14 @@ class Cylinder final : public ConvexRegionInterface /*! * An axis-alligned ellipsoid centered at the origin. */ -class Ellipsoid final : public ConvexRegionInterface +class Ellipsoid final : public IntersectRegionInterface { public: // Construct with radius explicit Ellipsoid(Real3 const& radii); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -205,9 +206,8 @@ class Ellipsoid final : public ConvexRegionInterface * on two parallel planes perpendicular to Z axis. * * TODO: Add proper treatment for degenerate cases. - * TODO: support twisted faces. */ -class GenTrap final : public ConvexRegionInterface +class GenTrap final : public IntersectRegionInterface { public: //!@{ @@ -246,7 +246,7 @@ class GenTrap final : public ConvexRegionInterface GenTrap(real_type halfz, VecReal2 const& lo, VecReal2 const& hi); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -276,14 +276,14 @@ class GenTrap final : public ConvexRegionInterface * subtracted, or its negation can be subtracted. The start angle is mapped * onto [0, 1) on construction. */ -class InfWedge final : public ConvexRegionInterface +class InfWedge final : public IntersectRegionInterface { public: // Construct from a starting angle and interior angle InfWedge(Turn start, Turn interior); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -323,14 +323,14 @@ class InfWedge final : public ConvexRegionInterface * - `phi:` azimuthal angle of the shape's main axis (as explained above). *   Validity range is `[0, 1)`. */ -class Parallelepiped final : public ConvexRegionInterface +class Parallelepiped final : public IntersectRegionInterface { public: // Construct with half widths and 3 angles Parallelepiped(Real3 const& halfedges, Turn alpha, Turn theta, Turn phi); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -376,7 +376,7 @@ class Parallelepiped final : public ConvexRegionInterface * - n=4 is a diamond * - n=6 is a pointy-top hexagon */ -class Prism final : public ConvexRegionInterface +class Prism final : public IntersectRegionInterface { public: // Construct with inner radius (apothem), half height, and orientation @@ -386,7 +386,7 @@ class Prism final : public ConvexRegionInterface real_type orientation); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; @@ -423,14 +423,14 @@ class Prism final : public ConvexRegionInterface * \note Be aware there's also a sphere *surface* at orange/surf/Sphere.hh in a * different namespace. */ -class Sphere final : public ConvexRegionInterface +class Sphere final : public IntersectRegionInterface { public: // Construct with radius explicit Sphere(real_type radius); // Build surfaces - void build(ConvexSurfaceBuilder&) const final; + void build(IntersectSurfaceBuilder&) const final; // Output to JSON void output(JsonPimpl*) const final; diff --git a/src/orange/orangeinp/ConvexSurfaceBuilder.cc b/src/orange/orangeinp/IntersectSurfaceBuilder.cc similarity index 85% rename from src/orange/orangeinp/ConvexSurfaceBuilder.cc rename to src/orange/orangeinp/IntersectSurfaceBuilder.cc index f8290d5847..c2760289e4 100644 --- a/src/orange/orangeinp/ConvexSurfaceBuilder.cc +++ b/src/orange/orangeinp/IntersectSurfaceBuilder.cc @@ -3,16 +3,16 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/ConvexSurfaceBuilder.cc +//! \file orange/orangeinp/IntersectSurfaceBuilder.cc //---------------------------------------------------------------------------// -#include "ConvexSurfaceBuilder.hh" +#include "IntersectSurfaceBuilder.hh" #include "orange/BoundingBoxUtils.hh" #include "orange/surf/RecursiveSimplifier.hh" #include "orange/surf/SurfaceClipper.hh" -#include "detail/ConvexSurfaceState.hh" #include "detail/CsgUnitBuilder.hh" +#include "detail/IntersectSurfaceState.hh" #include "detail/NegatedSurfaceClipper.hh" namespace celeritas @@ -51,9 +51,9 @@ struct ClipImpl * * Both arguments must have lifetimes that exceed the surface builder, but the * "unit builder" will have a duration of the whole unit construction, whereas - * the state just has the duration of the convex surface set being built. + * the state just has the duration of the surface set being built. */ -ConvexSurfaceBuilder::ConvexSurfaceBuilder(UnitBuilder* ub, State* state) +IntersectSurfaceBuilder::IntersectSurfaceBuilder(UnitBuilder* ub, State* state) : ub_{ub}, state_{state} { CELER_EXPECT(ub_ && state_); @@ -68,10 +68,10 @@ ConvexSurfaceBuilder::ConvexSurfaceBuilder(UnitBuilder* ub, State* state) /*! * Add a surface with a sense. * - * The resulting surface *MUST* result in a convex region. + * The resulting surface *MUST* result in a intersect region. */ template -void ConvexSurfaceBuilder::operator()(Sense sense, S const& surf) +void IntersectSurfaceBuilder::operator()(Sense sense, S const& surf) { // First, clip the local bounding zone based on the given surface RecursiveSimplifier clip_simplified_local(ClipImpl{&state_->local_bzone}, @@ -91,12 +91,12 @@ void ConvexSurfaceBuilder::operator()(Sense sense, S const& surf) * Add a surface after transforming it to an unknown type. * * \param extension Constructed metadata for the surface node - * \param sense Whether the convex region is inside/outside this surface + * \param sense Whether the intersect region is inside/outside this surface * \param surf Type-deleted surface */ -void ConvexSurfaceBuilder::insert_transformed(std::string&& extension, - Sense sense, - VariantSurface const& surf) +void IntersectSurfaceBuilder::insert_transformed(std::string&& extension, + Sense sense, + VariantSurface const& surf) { NodeId node_id; auto construct_impl = [&](Sense final_sense, auto&& final_surf) { @@ -138,7 +138,7 @@ void ConvexSurfaceBuilder::insert_transformed(std::string&& extension, /*! * Shrink the exterior bounding boxes. */ -void ConvexSurfaceBuilder::shrink_exterior(BBox const& bbox) +void IntersectSurfaceBuilder::shrink_exterior(BBox const& bbox) { CELER_EXPECT(bbox && !is_degenerate(bbox)); @@ -159,7 +159,7 @@ void ConvexSurfaceBuilder::shrink_exterior(BBox const& bbox) /*! * Grow the interior local bounding box. */ -void ConvexSurfaceBuilder::grow_interior(BBox const& bbox) +void IntersectSurfaceBuilder::grow_interior(BBox const& bbox) { CELER_EXPECT(bbox); @@ -180,9 +180,9 @@ void ConvexSurfaceBuilder::grow_interior(BBox const& bbox) // FREE FUNCTION DEFINITIONS //---------------------------------------------------------------------------// /*! - * Apply a convex surface builder to an unknown type. + * Apply an intersect surface builder to an unknown type. */ -void visit(ConvexSurfaceBuilder& csb, Sense sense, VariantSurface const& surf) +void visit(IntersectSurfaceBuilder& csb, Sense sense, VariantSurface const& surf) { std::visit([&csb, sense](auto const& s) { csb(sense, s); }, surf); } @@ -192,7 +192,7 @@ void visit(ConvexSurfaceBuilder& csb, Sense sense, VariantSurface const& surf) //---------------------------------------------------------------------------// //! \cond #define CSB_INSTANTIATE(SURF) \ - template void ConvexSurfaceBuilder::operator()(Sense, SURF const&) + template void IntersectSurfaceBuilder::operator()(Sense, SURF const&) CSB_INSTANTIATE(PlaneAligned); CSB_INSTANTIATE(PlaneAligned); CSB_INSTANTIATE(PlaneAligned); diff --git a/src/orange/orangeinp/ConvexSurfaceBuilder.hh b/src/orange/orangeinp/IntersectSurfaceBuilder.hh similarity index 79% rename from src/orange/orangeinp/ConvexSurfaceBuilder.hh rename to src/orange/orangeinp/IntersectSurfaceBuilder.hh index 498b0d86c5..8404a75ad7 100644 --- a/src/orange/orangeinp/ConvexSurfaceBuilder.hh +++ b/src/orange/orangeinp/IntersectSurfaceBuilder.hh @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/ConvexSurfaceBuilder.hh +//! \file orange/orangeinp/IntersectSurfaceBuilder.hh //---------------------------------------------------------------------------// #pragma once @@ -22,12 +22,12 @@ namespace orangeinp namespace detail { class CsgUnitBuilder; -struct ConvexSurfaceState; +struct IntersectSurfaceState; } // namespace detail //---------------------------------------------------------------------------// /*! - * Build a set of intersecting surfaces within a CSG node. + * Build a region of intersecting surfaces as a CSG node. * * This is the building block for constructing shapes, solids, and so forth. * The result of this class is: @@ -44,14 +44,8 @@ struct ConvexSurfaceState; * coordinate system * - \c RecursiveSimplifier to take transformed or user-input surfaces and * reduce them to more compact quadric representations - * - * \todo Should we require that the user implicitly guarantee that the result - * is convex, e.g. prohibit quadrics outside "saddle" points? What about a - * torus, which (unless degenerate) is never convex? Or should we just require - * that "exiting a surface exits the region"? (Think about application to OR-ed - * combinations for safety calculation.) */ -class ConvexSurfaceBuilder +class IntersectSurfaceBuilder { public: // Add a surface with negative quadric value being "inside" @@ -62,17 +56,17 @@ class ConvexSurfaceBuilder template void operator()(Sense sense, S const& surf); - // Promise that the convex surface is inside/outside this bbox + // Promise that the resulting region is inside/outside this bbox inline void operator()(Sense sense, BBox const& bbox); public: // "Private", to be used by testing and detail - using State = detail::ConvexSurfaceState; + using State = detail::IntersectSurfaceState; using UnitBuilder = detail::CsgUnitBuilder; using VecNode = std::vector; // Construct with persistent unit builder and less persistent state - ConvexSurfaceBuilder(UnitBuilder* ub, State* state); + IntersectSurfaceBuilder(UnitBuilder* ub, State* state); private: //// TYPES //// @@ -96,8 +90,10 @@ class ConvexSurfaceBuilder // FREE FUNCTIONS //---------------------------------------------------------------------------// -// Apply a convex surface builder to an unknown type -void visit(ConvexSurfaceBuilder& csb, Sense sense, VariantSurface const& surf); +// Apply a surface builder to an unknown type +void visit(IntersectSurfaceBuilder& csb, + Sense sense, + VariantSurface const& surf); //---------------------------------------------------------------------------// // INLINE FUNCTION DEFINITIONS @@ -106,7 +102,7 @@ void visit(ConvexSurfaceBuilder& csb, Sense sense, VariantSurface const& surf); * Add a surface with negative quadric value being "inside". */ template -void ConvexSurfaceBuilder::operator()(S const& surf) +void IntersectSurfaceBuilder::operator()(S const& surf) { return (*this)(Sense::inside, surf); } @@ -119,7 +115,7 @@ void ConvexSurfaceBuilder::operator()(S const& surf) * bbox. All bounding surfaces within the region must be *inside* the exterior * region and *outside* the interior region. */ -void ConvexSurfaceBuilder::operator()(Sense sense, BBox const& bbox) +void IntersectSurfaceBuilder::operator()(Sense sense, BBox const& bbox) { if (sense == Sense::inside) { diff --git a/src/orange/orangeinp/ObjectIO.json.cc b/src/orange/orangeinp/ObjectIO.json.cc index 14cc11ce02..e9be19c5b5 100644 --- a/src/orange/orangeinp/ObjectIO.json.cc +++ b/src/orange/orangeinp/ObjectIO.json.cc @@ -13,8 +13,8 @@ #include "corecel/cont/SpanIO.json.hh" #include "corecel/io/JsonPimpl.hh" -#include "ConvexRegion.hh" #include "CsgObject.hh" +#include "IntersectRegion.hh" #include "ObjectInterface.hh" #include "Shape.hh" #include "Solid.hh" @@ -157,8 +157,8 @@ void to_json(nlohmann::json& j, SolidEnclosedAngle const& sea) //---------------------------------------------------------------------------// //!@{ -//! Write convex regions to JSON -void to_json(nlohmann::json& j, ConvexRegionInterface const& cr) +//! Write intersect regions to JSON +void to_json(nlohmann::json& j, IntersectRegionInterface const& cr) { celeritas::JsonPimpl json_wrap; cr.output(&json_wrap); diff --git a/src/orange/orangeinp/ObjectIO.json.hh b/src/orange/orangeinp/ObjectIO.json.hh index 92d157c29d..50ba32173b 100644 --- a/src/orange/orangeinp/ObjectIO.json.hh +++ b/src/orange/orangeinp/ObjectIO.json.hh @@ -27,7 +27,7 @@ class Transformed; class SolidEnclosedAngle; -class ConvexRegionInterface; +class IntersectRegionInterface; class Box; class Cone; class Cylinder; @@ -54,8 +54,8 @@ void to_json(nlohmann::json& j, Transformed const& sb); // Write helper classes to JSON void to_json(nlohmann::json& j, SolidEnclosedAngle const& sea); -// Write convex regions to JSON -void to_json(nlohmann::json& j, ConvexRegionInterface const& cr); +// Write intersect regions to JSON +void to_json(nlohmann::json& j, IntersectRegionInterface const& cr); void to_json(nlohmann::json& j, Box const& cr); void to_json(nlohmann::json& j, Cone const& cr); void to_json(nlohmann::json& j, Cylinder const& cr); diff --git a/src/orange/orangeinp/Shape.cc b/src/orange/orangeinp/Shape.cc index d94d3157c4..66c58420a8 100644 --- a/src/orange/orangeinp/Shape.cc +++ b/src/orange/orangeinp/Shape.cc @@ -9,7 +9,7 @@ #include "corecel/io/JsonPimpl.hh" -#include "detail/BuildConvexRegion.hh" +#include "detail/BuildIntersectRegion.hh" #if CELERITAS_USE_JSON # include "ObjectIO.json.hh" @@ -25,7 +25,7 @@ namespace orangeinp */ NodeId ShapeBase::build(VolumeBuilder& vb) const { - return detail::build_convex_region( + return detail::build_intersect_region( vb, std::string{this->label()}, {}, this->interior()); } diff --git a/src/orange/orangeinp/Shape.hh b/src/orange/orangeinp/Shape.hh index 0e5eff78fa..a0b193fa9d 100644 --- a/src/orange/orangeinp/Shape.hh +++ b/src/orange/orangeinp/Shape.hh @@ -9,7 +9,7 @@ #include -#include "ConvexRegion.hh" +#include "IntersectRegion.hh" #include "ObjectInterface.hh" namespace celeritas @@ -18,14 +18,14 @@ namespace orangeinp { //---------------------------------------------------------------------------// /*! - * A simple, convex region of space. + * A simple, intersect-only region of space. * * This is an abstract class that implements \c build for constructing a volume * by dispatching to a method \c build_interior that the daughters must - * override using a convex region. + * override using a intersect region. * - * Use the implementation classes \c XShape where \c X is one of the convex - * region types in ConvexRegion.hh : + * Use the implementation classes \c XShape where \c X is one of the + * region types in IntersectRegion.hh : * - \c BoxShape * - \c ConeShape * - \c CylinderShape @@ -44,8 +44,8 @@ class ShapeBase : public ObjectInterface // Write the shape to JSON void output(JsonPimpl*) const final; - //! Interior convex region interface for construction and access - virtual ConvexRegionInterface const& interior() const = 0; + //! Interior intersect region interface for construction and access + virtual IntersectRegionInterface const& interior() const = 0; protected: //!@{ @@ -58,7 +58,7 @@ class ShapeBase : public ObjectInterface //---------------------------------------------------------------------------// /*! - * Shape that holds a convex region and forwards construction args to it. + * Shape that holds an intersect region and forwards construction args to it. * * Construct as: \code BoxShape s{"mybox", Real3{1, 2, 3}}; @@ -67,16 +67,16 @@ class ShapeBase : public ObjectInterface * Shape s{"mybox", Box{{1, 2, 3}}}; * \endcode * - * See ConvexRegion.hh for a list of the regions and their construction + * See IntersectRegion.hh for a list of the regions and their construction * arguments. */ template class Shape final : public ShapeBase { - static_assert(std::is_base_of_v); + static_assert(std::is_base_of_v); public: - //! Construct with a label and arguments of the convex region + //! Construct with a label and arguments of the intersect region template Shape(std::string&& label, Ts... region_args) : label_{std::move(label)}, region_{std::forward(region_args)...} @@ -84,7 +84,7 @@ class Shape final : public ShapeBase CELER_EXPECT(!label_.empty()); } - //! Construct with a label and convex region + //! Construct with a label and intersect region Shape(std::string&& label, T&& region) : label_{std::move(label)}, region_{std::move(region)} { @@ -94,8 +94,8 @@ class Shape final : public ShapeBase //! Get the user-provided label std::string_view label() const final { return label_; } - //! Interior convex region - ConvexRegionInterface const& interior() const final { return region_; } + //! Interior intersect region + IntersectRegionInterface const& interior() const final { return region_; } private: std::string label_; diff --git a/src/orange/orangeinp/Solid.cc b/src/orange/orangeinp/Solid.cc index a8adba560a..475ea193e8 100644 --- a/src/orange/orangeinp/Solid.cc +++ b/src/orange/orangeinp/Solid.cc @@ -10,11 +10,11 @@ #include "corecel/io/JsonPimpl.hh" #include "corecel/math/Algorithms.hh" -#include "ConvexSurfaceBuilder.hh" #include "CsgTreeUtils.hh" +#include "IntersectSurfaceBuilder.hh" #include "detail/BoundingZone.hh" -#include "detail/BuildConvexRegion.hh" +#include "detail/BuildIntersectRegion.hh" #include "detail/CsgUnitBuilder.hh" #include "detail/VolumeBuilder.hh" @@ -71,14 +71,14 @@ NodeId SolidBase::build(VolumeBuilder& vb) const std::vector nodes; // Build the outside-of-the-shell node - nodes.push_back(build_convex_region( + nodes.push_back(build_intersect_region( vb, std::string{this->label()}, "interior", this->interior())); if (auto* exclu = this->excluded()) { // Construct the excluded region by building a convex solid, then // negating it - NodeId smaller = build_convex_region( + NodeId smaller = build_intersect_region( vb, std::string{this->label()}, "excluded", *exclu); nodes.push_back(vb.insert_region({}, Negated{smaller})); } @@ -88,7 +88,7 @@ NodeId SolidBase::build(VolumeBuilder& vb) const // The enclosed angle is "true" (specified by the user to truncate the // shape azimuthally): construct a wedge to be added or deleted auto&& [sense, wedge] = sea.make_wedge(); - NodeId wedge_id = build_convex_region( + NodeId wedge_id = build_intersect_region( vb, std::string{this->label()}, "angle", wedge); if (sense == Sense::outside) { diff --git a/src/orange/orangeinp/Solid.hh b/src/orange/orangeinp/Solid.hh index b8f92c3d0c..6b8f8c30ac 100644 --- a/src/orange/orangeinp/Solid.hh +++ b/src/orange/orangeinp/Solid.hh @@ -13,7 +13,7 @@ #include "corecel/math/Turn.hh" -#include "ConvexRegion.hh" +#include "IntersectRegion.hh" #include "ObjectInterface.hh" namespace celeritas @@ -87,11 +87,11 @@ class SolidBase : public ObjectInterface // Write the shape to JSON void output(JsonPimpl*) const final; - //! Interior convex region interface for construction and access - virtual ConvexRegionInterface const& interior() const = 0; + //! Interior intersect region interface for construction and access + virtual IntersectRegionInterface const& interior() const = 0; //! Optional excluded region - virtual ConvexRegionInterface const* excluded() const = 0; + virtual IntersectRegionInterface const* excluded() const = 0; //! Angular restriction to add virtual SolidEnclosedAngle enclosed_angle() const = 0; @@ -122,7 +122,7 @@ class SolidBase : public ObjectInterface template class Solid final : public SolidBase { - static_assert(std::is_base_of_v); + static_assert(std::is_base_of_v); public: //!@{ @@ -152,11 +152,14 @@ class Solid final : public SolidBase //! Get the user-provided label std::string_view label() const final { return label_; } - //! Interior convex region interface for construction and access - ConvexRegionInterface const& interior() const final { return interior_; } + //! Interior intersect region interface for construction and access + IntersectRegionInterface const& interior() const final + { + return interior_; + } // Optional excluded - ConvexRegionInterface const* excluded() const final; + IntersectRegionInterface const* excluded() const final; //! Optional angular restriction SolidEnclosedAngle enclosed_angle() const final { return enclosed_; } @@ -199,7 +202,7 @@ SolidEnclosedAngle::operator bool() const * Access the optional excluded. */ template -ConvexRegionInterface const* Solid::excluded() const +IntersectRegionInterface const* Solid::excluded() const { return exclusion_ ? &(*exclusion_) : nullptr; } diff --git a/src/orange/orangeinp/detail/BuildConvexRegion.cc b/src/orange/orangeinp/detail/BuildIntersectRegion.cc similarity index 68% rename from src/orange/orangeinp/detail/BuildConvexRegion.cc rename to src/orange/orangeinp/detail/BuildIntersectRegion.cc index 31815edac3..815fa049dc 100644 --- a/src/orange/orangeinp/detail/BuildConvexRegion.cc +++ b/src/orange/orangeinp/detail/BuildIntersectRegion.cc @@ -3,16 +3,16 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/detail/BuildConvexRegion.cc +//! \file orange/orangeinp/detail/BuildIntersectRegion.cc //---------------------------------------------------------------------------// -#include "BuildConvexRegion.hh" +#include "BuildIntersectRegion.hh" -#include "orange/orangeinp/ConvexRegion.hh" -#include "orange/orangeinp/ConvexSurfaceBuilder.hh" +#include "orange/orangeinp/IntersectRegion.hh" +#include "orange/orangeinp/IntersectSurfaceBuilder.hh" #include "orange/surf/FaceNamer.hh" -#include "ConvexSurfaceState.hh" #include "CsgUnitBuilder.hh" +#include "IntersectSurfaceState.hh" #include "VolumeBuilder.hh" namespace celeritas @@ -23,21 +23,21 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Build a convex region. + * Build an intersect region. */ -NodeId build_convex_region(VolumeBuilder& vb, - std::string&& label, - std::string&& face_prefix, - ConvexRegionInterface const& region) +NodeId build_intersect_region(VolumeBuilder& vb, + std::string&& label, + std::string&& face_prefix, + IntersectRegionInterface const& region) { // Set input attributes for surface state - ConvexSurfaceState css; + IntersectSurfaceState css; css.transform = &vb.local_transform(); css.object_name = std::move(label); css.make_face_name = FaceNamer{std::move(face_prefix)}; // Construct surfaces - auto sb = ConvexSurfaceBuilder(&vb.unit_builder(), &css); + auto sb = IntersectSurfaceBuilder(&vb.unit_builder(), &css); region.build(sb); // Intersect the given surfaces to create a new CSG node diff --git a/src/orange/orangeinp/detail/BuildConvexRegion.hh b/src/orange/orangeinp/detail/BuildIntersectRegion.hh similarity index 55% rename from src/orange/orangeinp/detail/BuildConvexRegion.hh rename to src/orange/orangeinp/detail/BuildIntersectRegion.hh index f4e6f64edc..f2562a59b6 100644 --- a/src/orange/orangeinp/detail/BuildConvexRegion.hh +++ b/src/orange/orangeinp/detail/BuildIntersectRegion.hh @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/detail/BuildConvexRegion.hh +//! \file orange/orangeinp/detail/BuildIntersectRegion.hh //---------------------------------------------------------------------------// #pragma once @@ -15,24 +15,24 @@ namespace celeritas { namespace orangeinp { -class ConvexRegionInterface; +class IntersectRegionInterface; namespace detail { class VolumeBuilder; //---------------------------------------------------------------------------// -// Build a convex region -NodeId build_convex_region(VolumeBuilder& vb, - std::string&& label, - std::string&& face_prefix, - ConvexRegionInterface const& region); +// Build a intersect region +NodeId build_intersect_region(VolumeBuilder& vb, + std::string&& label, + std::string&& face_prefix, + IntersectRegionInterface const& region); -//! Build a convex region with no face prefix -inline NodeId build_convex_region(VolumeBuilder& vb, - std::string&& label, - ConvexRegionInterface const& region) +//! Build an intersect region with no face prefix +inline NodeId build_intersect_region(VolumeBuilder& vb, + std::string&& label, + IntersectRegionInterface const& region) { - return build_convex_region(vb, std::move(label), {}, region); + return build_intersect_region(vb, std::move(label), {}, region); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/detail/ConvexSurfaceState.cc b/src/orange/orangeinp/detail/IntersectSurfaceState.cc similarity index 90% rename from src/orange/orangeinp/detail/ConvexSurfaceState.cc rename to src/orange/orangeinp/detail/IntersectSurfaceState.cc index 1d0176d48f..d2c05218a2 100644 --- a/src/orange/orangeinp/detail/ConvexSurfaceState.cc +++ b/src/orange/orangeinp/detail/IntersectSurfaceState.cc @@ -3,9 +3,9 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/detail/ConvexSurfaceState.cc +//! \file orange/orangeinp/detail/IntersectSurfaceState.cc //---------------------------------------------------------------------------// -#include "ConvexSurfaceState.hh" +#include "IntersectSurfaceState.hh" #include "orange/transform/VariantTransform.hh" @@ -22,7 +22,7 @@ namespace detail * This requires that the local and global zones have been set, and the * transform be present. */ -BoundingZone calc_merged_bzone(ConvexSurfaceState const& css) +BoundingZone calc_merged_bzone(IntersectSurfaceState const& css) { CELER_EXPECT(css.transform); CELER_EXPECT( diff --git a/src/orange/orangeinp/detail/ConvexSurfaceState.hh b/src/orange/orangeinp/detail/IntersectSurfaceState.hh similarity index 89% rename from src/orange/orangeinp/detail/ConvexSurfaceState.hh rename to src/orange/orangeinp/detail/IntersectSurfaceState.hh index e4312f5d89..38235c6707 100644 --- a/src/orange/orangeinp/detail/ConvexSurfaceState.hh +++ b/src/orange/orangeinp/detail/IntersectSurfaceState.hh @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/detail/ConvexSurfaceState.hh +//! \file orange/orangeinp/detail/IntersectSurfaceState.hh //---------------------------------------------------------------------------// #pragma once @@ -29,7 +29,7 @@ namespace detail * Note that the surface clippers have *pointers* to local and global bounding * zones. Those must exceed the lifetime of this state. */ -struct ConvexSurfaceState +struct IntersectSurfaceState { //!@{ //! \name Input state @@ -43,7 +43,7 @@ struct ConvexSurfaceState //!@{ //! \name Output state - //! Local (to convex surface state) interior/exterior + //! Local (to intersecting surface state) interior/exterior BoundingZone local_bzone = BoundingZone::from_infinite(); //! Global (to unit) interior/exterior BoundingZone global_bzone = BoundingZone::from_infinite(); @@ -60,7 +60,7 @@ struct ConvexSurfaceState //---------------------------------------------------------------------------// // Use the local and global bounding zones to create a better zone -BoundingZone calc_merged_bzone(ConvexSurfaceState const& css); +BoundingZone calc_merged_bzone(IntersectSurfaceState const& css); //---------------------------------------------------------------------------// } // namespace detail diff --git a/test/orange/CMakeLists.txt b/test/orange/CMakeLists.txt index 3a3d911c72..adb62b232c 100644 --- a/test/orange/CMakeLists.txt +++ b/test/orange/CMakeLists.txt @@ -72,11 +72,11 @@ celeritas_add_test(detail/BIHUtils.test.cc) #-----------------------------------------------------------------------------# # Input construction set(CELERITASTEST_PREFIX orange/orangeinp) -celeritas_add_test(orangeinp/ConvexRegion.test.cc) -celeritas_add_test(orangeinp/ConvexSurfaceBuilder.test.cc) celeritas_add_test(orangeinp/CsgObject.test.cc) celeritas_add_test(orangeinp/CsgTree.test.cc) celeritas_add_test(orangeinp/CsgTreeUtils.test.cc) +celeritas_add_test(orangeinp/IntersectRegion.test.cc) +celeritas_add_test(orangeinp/IntersectSurfaceBuilder.test.cc) celeritas_add_test(orangeinp/Shape.test.cc) celeritas_add_test(orangeinp/Solid.test.cc) celeritas_add_test(orangeinp/Transformed.test.cc) diff --git a/test/orange/orangeinp/CsgTestUtils.cc b/test/orange/orangeinp/CsgTestUtils.cc index 811f21263c..e73a22ee40 100644 --- a/test/orange/orangeinp/CsgTestUtils.cc +++ b/test/orange/orangeinp/CsgTestUtils.cc @@ -20,8 +20,8 @@ #include "orange/BoundingBoxUtils.hh" #include "orange/orangeinp/CsgTree.hh" #include "orange/orangeinp/CsgTreeUtils.hh" -#include "orange/orangeinp/detail/ConvexSurfaceState.hh" #include "orange/orangeinp/detail/CsgUnit.hh" +#include "orange/orangeinp/detail/IntersectSurfaceState.hh" #include "orange/surf/SurfaceIO.hh" #include "orange/transform/TransformIO.hh" @@ -313,7 +313,7 @@ if (CELERITAS_USE_JSON) } //---------------------------------------------------------------------------// -void print_expected(ConvexSurfaceState const& css) +void print_expected(IntersectSurfaceState const& css) { std::cout << R"cpp( /***** EXPECTED STATE *****/ diff --git a/test/orange/orangeinp/CsgTestUtils.hh b/test/orange/orangeinp/CsgTestUtils.hh index ac5f572abd..3a11ce1e1a 100644 --- a/test/orange/orangeinp/CsgTestUtils.hh +++ b/test/orange/orangeinp/CsgTestUtils.hh @@ -21,7 +21,7 @@ class CsgTree; namespace detail { struct BoundingZone; -struct ConvexSurfaceState; +struct IntersectSurfaceState; struct CsgUnit; } // namespace detail @@ -42,7 +42,7 @@ std::vector fill_strings(detail::CsgUnit const& u); std::vector flattened(detail::BoundingZone const& bz); void print_expected(detail::CsgUnit const& u); -void print_expected(detail::ConvexSurfaceState const& css); +void print_expected(detail::IntersectSurfaceState const& css); //---------------------------------------------------------------------------// } // namespace test diff --git a/test/orange/orangeinp/ConvexRegion.test.cc b/test/orange/orangeinp/IntersectRegion.test.cc similarity index 96% rename from test/orange/orangeinp/ConvexRegion.test.cc rename to test/orange/orangeinp/IntersectRegion.test.cc index f3851bc0e8..6bc96cfc26 100644 --- a/test/orange/orangeinp/ConvexRegion.test.cc +++ b/test/orange/orangeinp/IntersectRegion.test.cc @@ -3,16 +3,16 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/ConvexRegion.test.cc +//! \file orange/orangeinp/IntersectRegion.test.cc //---------------------------------------------------------------------------// -#include "orange/orangeinp/ConvexRegion.hh" +#include "orange/orangeinp/IntersectRegion.hh" #include "orange/BoundingBoxUtils.hh" #include "orange/MatrixUtils.hh" -#include "orange/orangeinp/ConvexSurfaceBuilder.hh" #include "orange/orangeinp/CsgTreeUtils.hh" -#include "orange/orangeinp/detail/ConvexSurfaceState.hh" +#include "orange/orangeinp/IntersectSurfaceBuilder.hh" #include "orange/orangeinp/detail/CsgUnitBuilder.hh" +#include "orange/orangeinp/detail/IntersectSurfaceState.hh" #include "orange/orangeinp/detail/SenseEvaluator.hh" #include "CsgTestUtils.hh" @@ -31,12 +31,12 @@ namespace orangeinp namespace test { //---------------------------------------------------------------------------// -class ConvexRegionTest : public ::celeritas::test::Test +class IntersectRegionTest : public ::celeritas::test::Test { private: using Unit = orangeinp::detail::CsgUnit; using UnitBuilder = orangeinp::detail::CsgUnitBuilder; - using State = orangeinp::detail::ConvexSurfaceState; + using State = orangeinp::detail::IntersectSurfaceState; using Tol = UnitBuilder::Tol; protected: @@ -52,10 +52,10 @@ class ConvexRegionTest : public ::celeritas::test::Test }; protected: - TestResult test(ConvexRegionInterface const& r, VariantTransform const&); + TestResult test(IntersectRegionInterface const& r, VariantTransform const&); //! Test with no transform - TestResult test(ConvexRegionInterface const& r) + TestResult test(IntersectRegionInterface const& r) { return this->test(r, NoTransformation{}); } @@ -76,15 +76,15 @@ class ConvexRegionTest : public ::celeritas::test::Test }; //---------------------------------------------------------------------------// -auto ConvexRegionTest::test(ConvexRegionInterface const& r, - VariantTransform const& trans) -> TestResult +auto IntersectRegionTest::test(IntersectRegionInterface const& r, + VariantTransform const& trans) -> TestResult { - detail::ConvexSurfaceState css; + detail::IntersectSurfaceState css; css.transform = &trans; css.make_face_name = {}; css.object_name = "cr"; - ConvexSurfaceBuilder insert_surface{&unit_builder_, &css}; + IntersectSurfaceBuilder insert_surface{&unit_builder_, &css}; r.build(insert_surface); if (css.local_bzone.exterior || css.local_bzone.interior) { @@ -115,7 +115,7 @@ auto ConvexRegionTest::test(ConvexRegionInterface const& r, } //---------------------------------------------------------------------------// -void ConvexRegionTest::TestResult::print_expected() const +void IntersectRegionTest::TestResult::print_expected() const { using std::cout; cout << "/***** EXPECTED REGION *****/\n" @@ -145,7 +145,7 @@ void ConvexRegionTest::TestResult::print_expected() const //---------------------------------------------------------------------------// // BOX //---------------------------------------------------------------------------// -using BoxTest = ConvexRegionTest; +using BoxTest = IntersectRegionTest; TEST_F(BoxTest, errors) { @@ -180,7 +180,7 @@ TEST_F(BoxTest, standard) //---------------------------------------------------------------------------// // CONE //---------------------------------------------------------------------------// -using ConeTest = ConvexRegionTest; +using ConeTest = IntersectRegionTest; TEST_F(ConeTest, errors) { @@ -313,7 +313,7 @@ TEST_F(ConeTest, transformed) //---------------------------------------------------------------------------// // CYLINDER //---------------------------------------------------------------------------// -using CylinderTest = ConvexRegionTest; +using CylinderTest = IntersectRegionTest; TEST_F(CylinderTest, errors) { @@ -380,7 +380,7 @@ TEST_F(CylinderTest, transformed) //---------------------------------------------------------------------------// // ELLIPSOID //---------------------------------------------------------------------------// -using EllipsoidTest = ConvexRegionTest; +using EllipsoidTest = IntersectRegionTest; TEST_F(EllipsoidTest, errors) { @@ -410,7 +410,7 @@ TEST_F(EllipsoidTest, standard) //---------------------------------------------------------------------------// // GENTRAP //---------------------------------------------------------------------------// -using GenTrapTest = ConvexRegionTest; +using GenTrapTest = IntersectRegionTest; TEST_F(GenTrapTest, construct) { @@ -711,23 +711,24 @@ TEST_F(GenTrapTest, trap_full) // TODO: this should be valid TEST_F(GenTrapTest, DISABLED_pentahedron) { - auto result = this->test(GenTrap(3, {{-2,-2}, {3,0}, {-2,2}}, - {{-2,-1}, {-1,1}, {2,0}})); + auto result = this->test( + GenTrap(3, {{-2, -2}, {3, 0}, {-2, 2}}, {{-2, -1}, {-1, 1}, {2, 0}})); result.print_expected(); } // TODO: we may need to support this TEST_F(GenTrapTest, DISABLED_tetrahedron) { - auto result = this->test(GenTrap(3, {{-1,-1}, {2,0}, {-1,1}}, - {{0,0}, {0,0}, {0,0}})); + auto result = this->test( + GenTrap(3, {{-1, -1}, {2, 0}, {-1, 1}}, {{0, 0}, {0, 0}, {0, 0}})); } // TODO: find a valid set of points TEST_F(GenTrapTest, full) { - auto result = this->test(GenTrap(4, {{-2,-2}, {-2,2}, {2,2}, {2,-2}}, - {{-2,-2}, {-1,1}, {1,1}, {2,-2}})); + auto result = this->test(GenTrap(4, + {{-2, -2}, {-2, 2}, {2, 2}, {2, -2}}, + {{-2, -2}, {-1, 1}, {1, 1}, {2, -2}})); static char const expected_node[] = "all(+0, -1, -2, -3, -4, +5)"; static char const* const expected_surfaces[] @@ -843,7 +844,7 @@ TEST_F(GenTrapTest, adjacent_twisted) //---------------------------------------------------------------------------// // INFWEDGE //---------------------------------------------------------------------------// -using InfWedgeTest = ConvexRegionTest; +using InfWedgeTest = IntersectRegionTest; TEST_F(InfWedgeTest, errors) { @@ -946,7 +947,7 @@ TEST_F(InfWedgeTest, half_turn) //---------------------------------------------------------------------------// // PARALLELEPIPED //---------------------------------------------------------------------------// -using ParallelepipedTest = ConvexRegionTest; +using ParallelepipedTest = IntersectRegionTest; TEST_F(ParallelepipedTest, errors) { @@ -1065,7 +1066,7 @@ TEST_F(ParallelepipedTest, full) //---------------------------------------------------------------------------// // PRISM //---------------------------------------------------------------------------// -using PrismTest = ConvexRegionTest; +using PrismTest = IntersectRegionTest; TEST_F(PrismTest, errors) { @@ -1187,7 +1188,7 @@ TEST_F(PrismTest, rhex) //---------------------------------------------------------------------------// // SPHERE //---------------------------------------------------------------------------// -using SphereTest = ConvexRegionTest; +using SphereTest = IntersectRegionTest; TEST_F(SphereTest, errors) { diff --git a/test/orange/orangeinp/ConvexSurfaceBuilder.test.cc b/test/orange/orangeinp/IntersectSurfaceBuilder.test.cc similarity index 87% rename from test/orange/orangeinp/ConvexSurfaceBuilder.test.cc rename to test/orange/orangeinp/IntersectSurfaceBuilder.test.cc index 3223362fc3..c438437b9c 100644 --- a/test/orange/orangeinp/ConvexSurfaceBuilder.test.cc +++ b/test/orange/orangeinp/IntersectSurfaceBuilder.test.cc @@ -3,12 +3,12 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file orange/orangeinp/ConvexSurfaceBuilder.test.cc +//! \file orange/orangeinp/IntersectSurfaceBuilder.test.cc //---------------------------------------------------------------------------// -#include "orange/orangeinp/ConvexSurfaceBuilder.hh" +#include "orange/orangeinp/IntersectSurfaceBuilder.hh" #include "orange/MatrixUtils.hh" -#include "orange/orangeinp/detail/ConvexSurfaceState.hh" +#include "orange/orangeinp/detail/IntersectSurfaceState.hh" #include "CsgTestUtils.hh" #include "ObjectTestBase.hh" @@ -22,10 +22,10 @@ namespace test { //---------------------------------------------------------------------------// -class ConvexSurfaceBuilderTest : public ObjectTestBase +class IntersectSurfaceBuilderTest : public ObjectTestBase { protected: - using State = detail::ConvexSurfaceState; + using State = detail::IntersectSurfaceState; Tol tolerance() const override { return Tol::from_relative(1e-4); } @@ -41,19 +41,19 @@ class ConvexSurfaceBuilderTest : public ObjectTestBase VariantTransform transform_; }; -void x_slab(ConvexSurfaceBuilder& build) +void x_slab(IntersectSurfaceBuilder& build) { build(Sense::outside, PlaneX{-0.5}); build(Sense::inside, PlaneX{0.5}); } -TEST_F(ConvexSurfaceBuilderTest, no_transform) +TEST_F(IntersectSurfaceBuilderTest, no_transform) { { SCOPED_TRACE("z_hemi"); auto css = this->make_state(); css.object_name = "zh"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; build(PlaneZ{0.0}); build(SphereCentered{1.0}); @@ -71,7 +71,7 @@ TEST_F(ConvexSurfaceBuilderTest, no_transform) SCOPED_TRACE("reverse_hemi"); auto css = this->make_state(); css.object_name = "rh"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; build(Sense::outside, PlaneZ{1e-5}); build(Sense::inside, SphereCentered{1.0}); @@ -92,7 +92,7 @@ TEST_F(ConvexSurfaceBuilderTest, no_transform) SCOPED_TRACE("dedupe hemi"); auto css = this->make_state(); css.object_name = "dh"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; build(PlaneZ{1e-5}); build(SphereCentered{1.0}); @@ -110,7 +110,7 @@ TEST_F(ConvexSurfaceBuilderTest, no_transform) SCOPED_TRACE("slab"); auto css = this->make_state(); css.object_name = "sl"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; x_slab(build); static real_type const expected_local_bz[] = { @@ -142,14 +142,14 @@ TEST_F(ConvexSurfaceBuilderTest, no_transform) EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); } -TEST_F(ConvexSurfaceBuilderTest, translate) +TEST_F(IntersectSurfaceBuilderTest, translate) { transform_ = Translation{Real3{1, 2, 3}}; { SCOPED_TRACE("slab"); auto css = this->make_state(); css.object_name = "sl"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; x_slab(build); // clang-format off @@ -168,7 +168,7 @@ TEST_F(ConvexSurfaceBuilderTest, translate) SCOPED_TRACE("sphere"); auto css = this->make_state(); css.object_name = "sph"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; build(SphereCentered{1.0}); // clang-format off @@ -190,7 +190,7 @@ TEST_F(ConvexSurfaceBuilderTest, translate) SCOPED_TRACE("slab shared"); auto css = this->make_state(); css.object_name = "ss"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; x_slab(build); // clang-format off @@ -206,8 +206,12 @@ TEST_F(ConvexSurfaceBuilderTest, translate) EXPECT_VEC_EQ(expected_nodes, to_vec_int(css.nodes)); } - static char const * const expected_surface_strings[] = {"Plane: x=0.5", - "Plane: x=1.5", "Sphere: r=1 at {1,2,3}", "Plane: x=2.5"}; + static char const* const expected_surface_strings[] = { + "Plane: x=0.5", + "Plane: x=1.5", + "Sphere: r=1 at {1,2,3}", + "Plane: x=2.5", + }; static char const* const expected_md_strings[] = { "", "", "sl@c.mx", "sl@c.px,ss@c.mx", "", "sph@c.s", "", "ss@c.px", ""}; static char const expected_tree_string[] @@ -222,7 +226,7 @@ TEST_F(ConvexSurfaceBuilderTest, translate) } } -TEST_F(ConvexSurfaceBuilderTest, transform) +TEST_F(IntersectSurfaceBuilderTest, transform) { transform_ = Transformation{make_rotation(Axis::x, Turn{0.25}), Real3{0, 0, 1}}; @@ -230,7 +234,7 @@ TEST_F(ConvexSurfaceBuilderTest, transform) SCOPED_TRACE("hemi"); auto css = this->make_state(); css.object_name = "h"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; build(PlaneZ{1e-5}); build(SphereCentered{1.0}); @@ -249,8 +253,8 @@ TEST_F(ConvexSurfaceBuilderTest, transform) EXPECT_VEC_EQ(expected_nodes, to_vec_int(css.nodes)); } - static char const * const expected_surface_strings[] = {"Plane: y=0", - "Sphere: r=1 at {0,0,1}"}; + static char const* const expected_surface_strings[] + = {"Plane: y=0", "Sphere: r=1 at {0,0,1}"}; static char const expected_tree_string[] = R"json(["t",["~",0],["S",0],["S",1],["~",3]])json"; static char const* const expected_md_strings[] @@ -265,14 +269,14 @@ TEST_F(ConvexSurfaceBuilderTest, transform) } } -TEST_F(ConvexSurfaceBuilderTest, finite_extents) +TEST_F(IntersectSurfaceBuilderTest, finite_extents) { this->reset(BBox{{-10, -10, -10}, {10, 10, 10}}); { SCOPED_TRACE("slab"); auto css = this->make_state(); css.object_name = "sl"; - ConvexSurfaceBuilder build{&this->unit_builder(), &css}; + IntersectSurfaceBuilder build{&this->unit_builder(), &css}; x_slab(build); static real_type const expected_local_bz[] = { diff --git a/test/orange/orangeinp/detail/LocalSurfaceInserter.test.cc b/test/orange/orangeinp/detail/LocalSurfaceInserter.test.cc index 3d0120be83..4bca1e472d 100644 --- a/test/orange/orangeinp/detail/LocalSurfaceInserter.test.cc +++ b/test/orange/orangeinp/detail/LocalSurfaceInserter.test.cc @@ -106,7 +106,7 @@ TEST_F(LocalSurfaceInserterTest, chained_duplicates) EXPECT_EQ(5, surfaces.size()); } -// Replicates InfWedge.quarter_turn from convex region test +// Replicates InfWedge.quarter_turn from intersect region test TEST_F(LocalSurfaceInserterTest, infwedge_quadrant) { auto tol = Tolerance<>::from_relative(1e-4); From 8859fa44fa940225070b6b20b757df14218163cc Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Tue, 21 May 2024 17:40:39 -0400 Subject: [PATCH 55/59] Add RootPrimaryGenerator sampler to celer-sim (#1228) * Draft primary generator * Integrate with celer-sim * Draft RootEventSampler - Generate larger events by stacking multiple events from the original offloaded root file - Add capability of reading an individual event, given its event id, in RootEventReader - Make 'from_leaf' type of functions free helper functions for future classes * Integrate RootEventSampler to celer-sim * Add missing option --- app/celer-sim/Runner.cc | 20 ++++- app/celer-sim/RunnerInput.hh | 15 ++++ app/celer-sim/RunnerInputIO.json.cc | 21 +++++ app/celer-sim/RunnerInputIO.json.hh | 3 + app/celer-sim/Transporter.cc | 3 +- src/celeritas/CMakeLists.txt | 5 +- src/celeritas/ext/Convert.root.hh | 43 ++++++++++ src/celeritas/ext/RootImporter.cc | 8 +- src/celeritas/io/RootEventReader.cc | 111 ++++++++++++++++--------- src/celeritas/io/RootEventReader.hh | 26 +++--- src/celeritas/phys/RootEventSampler.cc | 66 +++++++++++++++ src/celeritas/phys/RootEventSampler.hh | 76 +++++++++++++++++ 12 files changed, 333 insertions(+), 64 deletions(-) create mode 100644 src/celeritas/ext/Convert.root.hh create mode 100644 src/celeritas/phys/RootEventSampler.cc create mode 100644 src/celeritas/phys/RootEventSampler.hh diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 8284ada605..8e0e37ffab 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -55,6 +55,7 @@ #include "celeritas/phys/PrimaryGeneratorOptions.hh" #include "celeritas/phys/Process.hh" #include "celeritas/phys/ProcessBuilder.hh" +#include "celeritas/phys/RootEventSampler.hh" #include "celeritas/random/RngParams.hh" #include "celeritas/track/SimParams.hh" #include "celeritas/track/TrackInitParams.hh" @@ -474,7 +475,21 @@ Runner::build_events(RunnerInput const& inp, SPConstParticles particles) } else if (ends_with(inp.event_file, ".root")) { - return read_events(RootEventReader(inp.event_file, particles)); + if (inp.file_sampling_options) + { + // Sampling options are assigned; use ROOT event sampler + return read_events( + RootEventSampler(inp.event_file, + particles, + inp.file_sampling_options.num_events, + inp.file_sampling_options.num_merged, + inp.seed)); + } + else + { + // Use event reader + return read_events(RootEventReader(inp.event_file, particles)); + } } else { @@ -598,8 +613,7 @@ auto Runner::get_transporter(StreamId stream) -> TransporterBase& /*! * Get an already-constructed transporter for the given stream. */ -auto Runner::get_transporter_ptr(StreamId stream) const - -> TransporterBase const* +auto Runner::get_transporter_ptr(StreamId stream) const -> TransporterBase const* { CELER_EXPECT(stream < transporters_.size()); return transporters_[stream.get()].get(); diff --git a/app/celer-sim/RunnerInput.hh b/app/celer-sim/RunnerInput.hh index 4dbc1e2234..930bb1e80e 100644 --- a/app/celer-sim/RunnerInput.hh +++ b/app/celer-sim/RunnerInput.hh @@ -39,6 +39,17 @@ namespace app */ struct RunnerInput { + struct EventFileSampling + { + size_type num_events{}; //!< Total number of events to sample + size_type num_merged{}; //!< ROOT file events per sampled event + + explicit operator bool() const + { + return num_events > 0 && num_merged > 0; + }; + }; + static constexpr Real3 no_field() { return Real3{0, 0, 0}; } static constexpr size_type unspecified{static_cast(-1)}; @@ -52,6 +63,10 @@ struct RunnerInput std::string physics_file; //!< Path to ROOT exported Geant4 data std::string event_file; //!< Path to input event data + // Optional setup when event_file is a ROOT input used for sampling + // combinations of events as opposed to just reading them + EventFileSampling file_sampling_options; //!< ROOT sampling options + // Optional setup options for generating primaries programmatically PrimaryGeneratorOptions primary_options; diff --git a/app/celer-sim/RunnerInputIO.json.cc b/app/celer-sim/RunnerInputIO.json.cc index 18786272c9..82de46defc 100644 --- a/app/celer-sim/RunnerInputIO.json.cc +++ b/app/celer-sim/RunnerInputIO.json.cc @@ -76,6 +76,8 @@ void from_json(nlohmann::json const& j, RunnerInput& v) LDIO_LOAD_OPTION(physics_file); LDIO_LOAD_OPTION(event_file); + LDIO_LOAD_OPTION(file_sampling_options); + LDIO_LOAD_DEPRECATED(primary_gen_options, primary_options); LDIO_LOAD_OPTION(primary_options); @@ -158,6 +160,9 @@ void to_json(nlohmann::json& j, RunnerInput const& v) LDIO_SAVE(geometry_file); LDIO_SAVE(physics_file); LDIO_SAVE_OPTION(event_file); + LDIO_SAVE_WHEN(file_sampling_options, + ends_with(v.event_file, ".root") + && static_cast(v.file_sampling_options)); LDIO_SAVE_WHEN(primary_options, v.event_file.empty()); LDIO_SAVE_OPTION(mctruth_file); @@ -196,6 +201,22 @@ void to_json(nlohmann::json& j, RunnerInput const& v) #undef LDIO_SAVE } +//---------------------------------------------------------------------------// +void from_json(nlohmann::json const& j, + app::RunnerInput::EventFileSampling& efs) +{ + CELER_JSON_LOAD_REQUIRED(j, efs, num_events); + CELER_JSON_LOAD_REQUIRED(j, efs, num_merged); +} + +void to_json(nlohmann::json& j, app::RunnerInput::EventFileSampling const& efs) +{ + j = nlohmann::json{ + CELER_JSON_PAIR(efs, num_events), + CELER_JSON_PAIR(efs, num_merged), + }; +} + //---------------------------------------------------------------------------// } // namespace app } // namespace celeritas diff --git a/app/celer-sim/RunnerInputIO.json.hh b/app/celer-sim/RunnerInputIO.json.hh index 5e6c58f9bc..b13cdb9dad 100644 --- a/app/celer-sim/RunnerInputIO.json.hh +++ b/app/celer-sim/RunnerInputIO.json.hh @@ -20,6 +20,9 @@ namespace app void to_json(nlohmann::json& j, RunnerInput const& value); void from_json(nlohmann::json const& j, RunnerInput& value); +void to_json(nlohmann::json& j, RunnerInput::EventFileSampling const& efs); +void from_json(nlohmann::json const& j, RunnerInput::EventFileSampling& efs); + //---------------------------------------------------------------------------// } // namespace app } // namespace celeritas diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index 2a97f7f22d..75a52926c3 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -79,8 +79,7 @@ void Transporter::operator()() * Transport the input primaries and all secondaries produced. */ template -auto Transporter::operator()(SpanConstPrimary primaries) - -> TransporterResult +auto Transporter::operator()(SpanConstPrimary primaries) -> TransporterResult { // Initialize results TransporterResult result; diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 0caf13e741..75a2d7f34c 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -198,9 +198,10 @@ if(CELERITAS_USE_ROOT) ext/ScopedRootErrorHandler.cc ext/RootUniquePtr.root.cc ext/RootFileManager.cc - user/RootStepWriter.cc io/RootEventReader.cc io/RootEventWriter.cc + phys/RootEventSampler.cc + user/RootStepWriter.cc "${CMAKE_CURRENT_BINARY_DIR}/CeleritasRootInterface.cxx" ) @@ -305,5 +306,3 @@ celeritas_target_link_libraries(celeritas ) #-----------------------------------------------------------------------------# - - diff --git a/src/celeritas/ext/Convert.root.hh b/src/celeritas/ext/Convert.root.hh new file mode 100644 index 0000000000..3adbbb87a4 --- /dev/null +++ b/src/celeritas/ext/Convert.root.hh @@ -0,0 +1,43 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/ext/Convert.root.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/Macros.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +// FREE FUNCTIONS +//---------------------------------------------------------------------------// +/*! + * Fetch single-dimension leaves. + */ +template +inline auto from_leaf(TLeaf const& leaf) -> T +{ + CELER_EXPECT(!leaf.IsZombie()); + return static_cast(leaf.GetValue()); +} + +//---------------------------------------------------------------------------// +/*! + * Fetch leaves containing `std::array`. + */ +inline Real3 from_array_leaf(TLeaf const& leaf) +{ + CELER_EXPECT(!leaf.IsZombie()); + CELER_ASSERT(leaf.GetLen() == 3); + return {static_cast(leaf.GetValue(0)), + static_cast(leaf.GetValue(1)), + static_cast(leaf.GetValue(2))}; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/ext/RootImporter.cc b/src/celeritas/ext/RootImporter.cc index 4df979feec..67bed32b2b 100644 --- a/src/celeritas/ext/RootImporter.cc +++ b/src/celeritas/ext/RootImporter.cc @@ -51,8 +51,12 @@ ImportData RootImporter::operator()() ScopedMem record_mem("RootImporter.read"); ScopedTimeLog scoped_time; - std::unique_ptr tree_data(root_input_->Get(tree_name())); - CELER_ASSERT(tree_data); + std::unique_ptr tree_data( + root_input_->Get(this->tree_name())); + CELER_VALIDATE(tree_data, + << "Cannot find TTree '" << this->tree_name() + << "' in ROOT file '" << root_input_->GetName() + << "'. Verify input file"); CELER_ASSERT(tree_data->GetEntries() == 1); ImportData import_data; diff --git a/src/celeritas/io/RootEventReader.cc b/src/celeritas/io/RootEventReader.cc index dfe37cb44b..f5d3799b75 100644 --- a/src/celeritas/io/RootEventReader.cc +++ b/src/celeritas/io/RootEventReader.cc @@ -8,10 +8,10 @@ #include "RootEventReader.hh" #include -#include #include #include "corecel/io/Logger.hh" +#include "celeritas/ext/Convert.root.hh" #include "celeritas/ext/ScopedRootErrorHandler.hh" #include "celeritas/phys/ParticleParams.hh" @@ -30,20 +30,75 @@ RootEventReader::RootEventReader(std::string const& filename, tfile_.reset(TFile::Open(filename.c_str(), "read")); CELER_ASSERT(tfile_->IsOpen()); - ttree_.reset(tfile_->Get(tree_name())); - CELER_ASSERT(ttree_); + ttree_.reset(tfile_->Get(this->tree_name())); + CELER_VALIDATE(ttree_, + << "TTree '" << this->tree_name() + << "' not found. Verify that '" << filename + << "' is a valid input file with Celeritas primary " + "offloaded data"); num_entries_ = ttree_->GetEntries(); CELER_ASSERT(num_entries_ > 0); // Get the number of events. Event IDs are sequential starting from zero. // The last entry will contain the largest event ID. ttree_->GetEntry(num_entries_ - 1); - num_events_ = this->from_leaf("event_id") + 1; + num_events_ = from_leaf(*ttree_->GetLeaf("event_id")) + 1; CELER_LOG(debug) << "ROOT file has " << num_events_ << " events"; scoped_root_error.throw_if_errors(); } +//---------------------------------------------------------------------------// +/*! + * Read a specific single event from the primaries tree. + */ +auto RootEventReader::operator()(EventId event_id) -> result_type +{ + CELER_EXPECT(event_id < num_events_); + + if (event_id < event_to_entry_.size()) + { + // Cached event entry; Load entry and return event + entry_count_ = event_to_entry_[event_id.get()]; + return this->operator()(); + } + else + { + // Continue from latest entry count + entry_count_ = event_to_entry_.back(); + } + + ScopedRootErrorHandler scoped_root_error; + + // Enable only event_id branch + ttree_->SetBranchStatus("*", false); // Disable all branches + ttree_->SetBranchStatus("event_id", true); + + EventId entry_event_id; + do + { + ttree_->GetEntry(entry_count_); + entry_event_id + = EventId{from_leaf(*ttree_->GetLeaf("event_id"))}; + + if (entry_event_id != expected_event_id_) + { + // Found new event; Add its entry to the cache + CELER_ASSERT(entry_event_id.get() == expected_event_id_.get() + 1); + event_to_entry_.push_back(entry_count_); + expected_event_id_ = entry_event_id; + } + + entry_count_++; + } while (entry_event_id != event_id); + + scoped_root_error.throw_if_errors(); + + auto result = this->operator()(); + CELER_ENSURE(!result.empty()); + return result; +} + //---------------------------------------------------------------------------// /*! * Read single event from the primaries tree. @@ -51,17 +106,19 @@ RootEventReader::RootEventReader(std::string const& filename, auto RootEventReader::operator()() -> result_type { CELER_EXPECT(entry_count_ <= num_entries_); - ScopedRootErrorHandler scoped_root_error; EventId expected_evt_id{0}; TrackId track_id{0}; result_type primaries; + ScopedRootErrorHandler scoped_root_error; + ttree_->SetBranchStatus("*", true); // Enable all branches for (; entry_count_ < num_entries_; entry_count_++) { ttree_->GetEntry(entry_count_); - auto entry_evt_id = EventId{this->from_leaf("event_id")}; + auto entry_evt_id + = EventId{from_leaf(*ttree_->GetLeaf("event_id"))}; if (primaries.empty()) { // First entry; set current event id @@ -76,13 +133,13 @@ auto RootEventReader::operator()() -> result_type Primary primary; primary.track_id = track_id; primary.event_id = expected_evt_id; - primary.particle_id - = params_->find(PDGNumber{this->from_leaf("particle")}); - primary.energy - = units::MevEnergy{this->from_leaf("energy")}; - primary.time = this->from_leaf("time"); - primary.position = this->from_array_leaf("pos"); - primary.direction = this->from_array_leaf("dir"); + primary.particle_id = params_->find( + PDGNumber{from_leaf(*ttree_->GetLeaf("particle"))}); + primary.energy = units::MevEnergy{ + from_leaf(*ttree_->GetLeaf("energy"))}; + primary.time = from_leaf(*ttree_->GetLeaf("time")); + primary.position = from_array_leaf(*ttree_->GetLeaf("pos")); + primary.direction = from_array_leaf(*ttree_->GetLeaf("dir")); primaries.push_back(std::move(primary)); track_id++; @@ -94,33 +151,5 @@ auto RootEventReader::operator()() -> result_type return primaries; } -//---------------------------------------------------------------------------// -/*! - * Helper function to fetch leaves. - */ -template -auto RootEventReader::from_leaf(char const* leaf_name) -> T -{ - CELER_EXPECT(ttree_); - auto const leaf = ttree_->GetLeaf(leaf_name); - CELER_ASSERT(leaf); - return static_cast(leaf->GetValue()); -} - -//---------------------------------------------------------------------------// -/*! - * Helper function to fetch leaves containing `std::array`. - */ -Real3 RootEventReader::from_array_leaf(char const* leaf_name) -{ - CELER_EXPECT(ttree_); - auto const leaf = ttree_->GetLeaf(leaf_name); - CELER_ASSERT(leaf); - CELER_ASSERT(leaf->GetLen() == 3); - return {static_cast(leaf->GetValue(0)), - static_cast(leaf->GetValue(1)), - static_cast(leaf->GetValue(2))}; -} - //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/io/RootEventReader.hh b/src/celeritas/io/RootEventReader.hh index d0e10cfb79..9c4bd31d96 100644 --- a/src/celeritas/io/RootEventReader.hh +++ b/src/celeritas/io/RootEventReader.hh @@ -48,6 +48,9 @@ class RootEventReader : public EventReaderInterface //! Prevent copying and moving CELER_DELETE_COPY_MOVE(RootEventReader); + // Read a user-defined event from the ROOT file + result_type operator()(EventId event_id); + // Read a single event from the ROOT file result_type operator()() final; @@ -58,23 +61,18 @@ class RootEventReader : public EventReaderInterface //// DATA //// SPConstParticles params_; - std::size_t entry_count_{0}; // Current TTree entry - std::size_t num_entries_; // Total number of entries in the TTree - size_type num_events_; // Total number of events UPExtern tfile_; UPExtern ttree_; + std::size_t num_entries_; // Total number of entries in the TTree + size_type num_events_; // Total number of events + std::size_t entry_count_{0}; // Current TTree entry + EventId expected_event_id_{0}; // Last event ID read + std::vector event_to_entry_{0}; // Cache event tree entry ids //// HELPER FUNCTIONS //// // Hardcoded ROOT TTree name defined by RootEventWriter char const* tree_name() { return "primaries"; } - - // Fetch basic data types from leaves - template - auto from_leaf(char const* leaf_name) -> T; - - // Fetch arrays from leaves - Real3 from_array_leaf(char const* leaf_name); }; //---------------------------------------------------------------------------// @@ -82,11 +80,13 @@ class RootEventReader : public EventReaderInterface inline RootEventReader::RootEventReader(std::string const&, SPConstParticles) { CELER_DISCARD(params_); - CELER_DISCARD(entry_count_); - CELER_DISCARD(num_entries_); - CELER_DISCARD(num_events_); CELER_DISCARD(tfile_); CELER_DISCARD(ttree_); + CELER_DISCARD(num_entries_); + CELER_DISCARD(num_events_); + CELER_DISCARD(entry_count_); + CELER_DISCARD(expected_event_id_); + CELER_DISCARD(event_to_entry_); CELER_NOT_CONFIGURED("ROOT"); } diff --git a/src/celeritas/phys/RootEventSampler.cc b/src/celeritas/phys/RootEventSampler.cc new file mode 100644 index 0000000000..aa6762d89c --- /dev/null +++ b/src/celeritas/phys/RootEventSampler.cc @@ -0,0 +1,66 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/phys/RootEventSampler.cc +//---------------------------------------------------------------------------// +#include "RootEventSampler.hh" + +#include + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct \c RootEventReader , and initialize sampling conditions. + */ +RootEventSampler::RootEventSampler(std::string const& filename, + SPConstParticles particles, + size_type num_sampled_events, + size_type num_merged_events, + unsigned int seed) + : num_sampled_events_(num_sampled_events) + , num_merged_events_(num_merged_events) +{ + CELER_EXPECT(!filename.empty()); + CELER_EXPECT(particles); + + reader_ = std::make_unique(filename, std::move(particles)); + CELER_EXPECT(num_merged_events_ > 0 + && num_merged_events_ <= reader_->num_events()); + + rng_.seed(seed); + select_event_ + = std::uniform_int_distribution(0, reader_->num_events()); +} + +//---------------------------------------------------------------------------// +/*! + * Return a vector of sampled primaries. + */ +auto RootEventSampler::operator()() -> result_type +{ + if (event_count_.get() == num_sampled_events_) + { + return {}; + } + + result_type result; + for ([[maybe_unused]] auto i : range(num_merged_events_)) + { + // Select a random event and overwrite its event id + auto event = reader_->operator()(EventId{select_event_(rng_)}); + std::for_each( + event.begin(), event.end(), [ec = event_count_](auto& primary) { + primary.event_id = ec; + }); + result.insert(result.end(), event.begin(), event.end()); + } + event_count_++; + + return result; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/phys/RootEventSampler.hh b/src/celeritas/phys/RootEventSampler.hh new file mode 100644 index 0000000000..9165eadda9 --- /dev/null +++ b/src/celeritas/phys/RootEventSampler.hh @@ -0,0 +1,76 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/phys/RootEventSampler.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "celeritas/io/EventIOInterface.hh" +#include "celeritas/io/RootEventReader.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Use \c RootEventReader to load events and sample primaries from them. + */ +class RootEventSampler : public EventReaderInterface +{ + public: + //!@{ + //! \name Type aliases + using SPConstParticles = std::shared_ptr; + using UPRootEventReader = std::unique_ptr; + using result_type = std::vector; + //!@} + + public: + // Construct with input data for RootEventReader and sampling information + RootEventSampler(std::string const& filename, + SPConstParticles particles, + size_type num_sampled_events, + size_type num_merged_events, + unsigned int seed); + + //! Sample primaries for a single event + result_type operator()() final; + + //! Get total number of events + size_type num_events() const final { return num_sampled_events_; } + + private: + size_type num_sampled_events_; // Total number of events + size_type num_merged_events_; // Number of events to be concatenated + UPRootEventReader reader_; + std::mt19937 rng_; + std::uniform_int_distribution select_event_; + EventId event_count_{0}; +}; + +//---------------------------------------------------------------------------// +#if !CELERITAS_USE_ROOT +inline RootEventSampler::RootEventSampler( + std::string const&, SPConstParticles, size_type, size_type, unsigned int) +{ + CELER_DISCARD(num_sampled_events_); + CELER_DISCARD(num_merged_events_); + CELER_DISCARD(reader_); + CELER_DISCARD(rng_); + CELER_DISCARD(select_event_); + CELER_DISCARD(event_count_); + CELER_NOT_CONFIGURED("ROOT"); +} + +inline RootEventSampler::result_type RootEventSampler::operator()() +{ + CELER_ASSERT_UNREACHABLE(); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace celeritas From 2da467875d8d35c3aeb0c9f30b09290e9ef44144 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Wed, 22 May 2024 14:56:40 +0100 Subject: [PATCH 56/59] Minor refactoring/niceties for upcoming polycone work (#1246) * Move vectorutils to corecel * Genericise monotonic checks * Make Celeritas::array unpackable * Add soft equivalence to surface and volume builder * Rename wedge methods in solid converter --- app/celer-sim/Transporter.cc | 2 +- src/celeritas/CMakeLists.txt | 1 - .../ext/detail/GeantModelImporter.cc | 2 +- src/celeritas/grid/ValueGridBuilder.cc | 2 +- .../optical/CerenkovDndxCalculator.hh | 2 +- .../optical/OpticalPropertyParams.cc | 2 +- src/corecel/CMakeLists.txt | 1 + src/corecel/cont/Array.hh | 39 +++++++++++++ .../grid/VectorUtils.cc | 20 +------ .../grid/VectorUtils.hh | 28 +++++++++- src/corecel/math/Algorithms.hh | 20 +++++++ src/orange/g4org/SolidConverter.cc | 55 +++++++++++++------ src/orange/orangeinp/IntersectRegion.cc | 3 + .../orangeinp/IntersectSurfaceBuilder.cc | 9 +++ .../orangeinp/IntersectSurfaceBuilder.hh | 9 +++ src/orange/orangeinp/Solid.hh | 2 +- src/orange/orangeinp/detail/VolumeBuilder.cc | 9 +++ src/orange/orangeinp/detail/VolumeBuilder.hh | 4 ++ test/celeritas/CMakeLists.txt | 1 - test/celeritas/optical/Cerenkov.test.cc | 2 +- test/corecel/CMakeLists.txt | 1 + .../grid/VectorUtils.test.cc | 4 +- test/corecel/math/Algorithms.test.cc | 24 ++++++++ 23 files changed, 193 insertions(+), 49 deletions(-) rename src/{celeritas => corecel}/grid/VectorUtils.cc (82%) rename src/{celeritas => corecel}/grid/VectorUtils.hh (63%) rename test/{celeritas => corecel}/grid/VectorUtils.test.cc (97%) diff --git a/app/celer-sim/Transporter.cc b/app/celer-sim/Transporter.cc index 75a52926c3..75dfbe8799 100644 --- a/app/celer-sim/Transporter.cc +++ b/app/celer-sim/Transporter.cc @@ -15,6 +15,7 @@ #include "corecel/Assert.hh" #include "corecel/cont/Range.hh" #include "corecel/data/Ref.hh" +#include "corecel/grid/VectorUtils.hh" #include "corecel/io/Logger.hh" #include "corecel/io/ScopedTimeLog.hh" #include "corecel/sys/ScopedSignalHandler.hh" @@ -22,7 +23,6 @@ #include "celeritas/global/CoreParams.hh" #include "celeritas/global/Stepper.hh" #include "celeritas/global/detail/ActionSequence.hh" -#include "celeritas/grid/VectorUtils.hh" #include "celeritas/phys/Model.hh" #include "StepTimer.hh" diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 75a2d7f34c..87710fdf05 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -46,7 +46,6 @@ list(APPEND SOURCES grid/ValueGridBuilder.cc grid/ValueGridInserter.cc grid/ValueGridType.cc - grid/VectorUtils.cc io/AtomicRelaxationReader.cc io/ImportData.cc io/ImportMaterial.cc diff --git a/src/celeritas/ext/detail/GeantModelImporter.cc b/src/celeritas/ext/detail/GeantModelImporter.cc index accaea651f..520331099e 100644 --- a/src/celeritas/ext/detail/GeantModelImporter.cc +++ b/src/celeritas/ext/detail/GeantModelImporter.cc @@ -20,9 +20,9 @@ #include "corecel/Assert.hh" #include "corecel/cont/EnumArray.hh" #include "corecel/cont/Range.hh" +#include "corecel/grid/VectorUtils.hh" #include "corecel/io/Logger.hh" #include "corecel/sys/TypeDemangler.hh" -#include "celeritas/grid/VectorUtils.hh" #include "GeantMicroXsCalculator.hh" diff --git a/src/celeritas/grid/ValueGridBuilder.cc b/src/celeritas/grid/ValueGridBuilder.cc index 1307134891..70e04e39e6 100644 --- a/src/celeritas/grid/ValueGridBuilder.cc +++ b/src/celeritas/grid/ValueGridBuilder.cc @@ -15,10 +15,10 @@ #include "corecel/cont/Range.hh" #include "corecel/grid/UniformGrid.hh" #include "corecel/grid/UniformGridData.hh" +#include "corecel/grid/VectorUtils.hh" #include "corecel/math/SoftEqual.hh" #include "ValueGridInserter.hh" -#include "VectorUtils.hh" namespace celeritas { diff --git a/src/celeritas/optical/CerenkovDndxCalculator.hh b/src/celeritas/optical/CerenkovDndxCalculator.hh index 8b56546545..cda37b3f9c 100644 --- a/src/celeritas/optical/CerenkovDndxCalculator.hh +++ b/src/celeritas/optical/CerenkovDndxCalculator.hh @@ -12,9 +12,9 @@ #include "corecel/Assert.hh" #include "corecel/Macros.hh" #include "corecel/Types.hh" +#include "corecel/grid/VectorUtils.hh" #include "celeritas/Quantities.hh" #include "celeritas/grid/GenericCalculator.hh" -#include "celeritas/grid/VectorUtils.hh" #include "CerenkovData.hh" #include "OpticalPropertyData.hh" diff --git a/src/celeritas/optical/OpticalPropertyParams.cc b/src/celeritas/optical/OpticalPropertyParams.cc index 2a5ca82916..939d73fe6d 100644 --- a/src/celeritas/optical/OpticalPropertyParams.cc +++ b/src/celeritas/optical/OpticalPropertyParams.cc @@ -12,12 +12,12 @@ #include "corecel/cont/Range.hh" #include "corecel/data/CollectionBuilder.hh" +#include "corecel/grid/VectorUtils.hh" #include "corecel/math/Algorithms.hh" #include "celeritas/Quantities.hh" #include "celeritas/Types.hh" #include "celeritas/grid/GenericGridBuilder.hh" #include "celeritas/grid/GenericGridData.hh" -#include "celeritas/grid/VectorUtils.hh" #include "celeritas/io/ImportData.hh" namespace celeritas diff --git a/src/corecel/CMakeLists.txt b/src/corecel/CMakeLists.txt index 3cfe73aaf4..6b190b2db6 100644 --- a/src/corecel/CMakeLists.txt +++ b/src/corecel/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCES data/Copier.cc data/DeviceAllocation.cc data/PinnedAllocator.cc + grid/VectorUtils.cc io/BuildOutput.cc io/ColorUtils.cc io/ExceptionOutput.cc diff --git a/src/corecel/cont/Array.hh b/src/corecel/cont/Array.hh index 299e7cb8da..7393a821cf 100644 --- a/src/corecel/cont/Array.hh +++ b/src/corecel/cont/Array.hh @@ -7,6 +7,9 @@ //---------------------------------------------------------------------------// #pragma once +#include +#include + #include "corecel/Macros.hh" #include "corecel/Types.hh" @@ -67,6 +70,22 @@ struct Array CELER_CONSTEXPR_FUNCTION reference back() { return data_[N - 1]; } CELER_CONSTEXPR_FUNCTION const_pointer data() const { return data_; } CELER_CONSTEXPR_FUNCTION pointer data() { return data_; } + + //! Access for structured unpacking + template + CELER_CONSTEXPR_FUNCTION T& get() + { + static_assert(I < static_cast(N)); + return data_[I]; + } + + //! Access for structured unpacking + template + CELER_CONSTEXPR_FUNCTION T const& get() const + { + static_assert(I < static_cast(N)); + return data_[I]; + } //!@} //!@{ @@ -127,3 +146,23 @@ operator!=(Array const& lhs, Array const& rhs) //---------------------------------------------------------------------------// } // namespace celeritas + +namespace std +{ +//---------------------------------------------------------------------------// +//! Support structured binding: array size +template +struct tuple_size> +{ + static constexpr std::size_t value = N; +}; + +//! Support structured binding: array element type +template +struct tuple_element> +{ + using type = T; +}; + +//---------------------------------------------------------------------------// +} // namespace std diff --git a/src/celeritas/grid/VectorUtils.cc b/src/corecel/grid/VectorUtils.cc similarity index 82% rename from src/celeritas/grid/VectorUtils.cc rename to src/corecel/grid/VectorUtils.cc index 9a156127bc..72053b63de 100644 --- a/src/celeritas/grid/VectorUtils.cc +++ b/src/corecel/grid/VectorUtils.cc @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/grid/VectorUtils.cc +//! \file corecel/grid/VectorUtils.cc //---------------------------------------------------------------------------// #include "VectorUtils.hh" @@ -67,23 +67,5 @@ std::vector logspace(double start, double stop, size_type n) return space_impl(start, stop, n); } -//---------------------------------------------------------------------------// -/*! - * True if the grid values are monotonically increasing. - */ -bool is_monotonic_increasing(Span grid) -{ - CELER_EXPECT(!grid.empty()); - auto iter = grid.begin(); - auto prev = *iter++; - while (iter != grid.end()) - { - if (*iter <= prev) - return false; - prev = *iter++; - } - return true; -} - //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/grid/VectorUtils.hh b/src/corecel/grid/VectorUtils.hh similarity index 63% rename from src/celeritas/grid/VectorUtils.hh rename to src/corecel/grid/VectorUtils.hh index 689d26ba71..4334e907a0 100644 --- a/src/celeritas/grid/VectorUtils.hh +++ b/src/corecel/grid/VectorUtils.hh @@ -3,15 +3,17 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/grid/VectorUtils.hh +//! \file corecel/grid/VectorUtils.hh //! \brief Grid creation helpers //---------------------------------------------------------------------------// #pragma once +#include #include #include "corecel/Types.hh" #include "corecel/cont/Span.hh" +#include "corecel/math/Algorithms.hh" namespace celeritas { @@ -24,8 +26,28 @@ std::vector linspace(double start, double stop, size_type n); std::vector logspace(double start, double stop, size_type n); //---------------------------------------------------------------------------// -// True if the grid values are monotonically increasing -bool is_monotonic_increasing(Span grid); +/*! + * True if the grid values are monotonically nondecreasing. + */ +template +inline bool is_monotonic_nondecreasing(Span grid) +{ + return all_adjacent(grid.begin(), grid.end(), [](T& left, T& right) { + return left <= right; + }); +} + +//---------------------------------------------------------------------------// +/*! + * True if the grid values are monotonically increasing. + */ +template +bool is_monotonic_increasing(Span grid) +{ + return all_adjacent(grid.begin(), grid.end(), [](T& left, T& right) { + return left < right; + }); +} //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/corecel/math/Algorithms.hh b/src/corecel/math/Algorithms.hh index cb23377bc3..bae94c29aa 100644 --- a/src/corecel/math/Algorithms.hh +++ b/src/corecel/math/Algorithms.hh @@ -140,6 +140,26 @@ inline CELER_FUNCTION bool any_of(InputIt iter, InputIt last, Predicate p) return false; } +//---------------------------------------------------------------------------// +/*! + * Whether the predicate is true for pairs of consecutive items. + */ +template +inline CELER_FUNCTION bool all_adjacent(InputIt iter, InputIt last, Predicate p) +{ + if (iter == last) + return true; + + auto prev = *iter++; + while (iter != last) + { + if (!p(prev, *iter)) + return false; + prev = *iter++; + } + return true; +} + //---------------------------------------------------------------------------// /*! * Clamp the value between lo and hi values. diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index 6b4b1f8964..080b847ec2 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -75,12 +75,27 @@ namespace * This internally converts from native Geant4 radians. */ template -SolidEnclosedAngle get_azimuthal_wedge(S const& solid) +SolidEnclosedAngle make_wedge_azimuthal(S const& solid) { return SolidEnclosedAngle{native_value_to(solid.GetStartPhiAngle()), native_value_to(solid.GetDeltaPhiAngle())}; } +//---------------------------------------------------------------------------// +/*! + * Get the enclosed azimuthal angle by a "poly" solid. + * + * Geant4 uses different function names for polycone, generic polycone, and + * polyhedra... + */ +template +SolidEnclosedAngle make_wedge_azimuthal_poly(S const& solid) +{ + auto start = native_value_to(solid.GetStartPhi()); + auto stop = native_value_to(solid.GetEndPhi()); + return SolidEnclosedAngle{start, stop - start}; +} + //---------------------------------------------------------------------------// /*! * Get the enclosed polar angle by a solid. @@ -88,7 +103,7 @@ SolidEnclosedAngle get_azimuthal_wedge(S const& solid) * This internally converts from native Geant4 radians. */ template -SolidEnclosedAngle get_polar_wedge(S const& solid) +SolidEnclosedAngle make_wedge_polar(S const& solid) { return SolidEnclosedAngle{ native_value_to(solid.GetStartThetaAngle()), @@ -147,12 +162,23 @@ auto calculate_theta_phi(S const& solid) -> std::pair * Construct a shape using the solid's name and forwarded arguments. */ template -auto make_shape(G4VSolid const& solid, Args&&... args) +auto make_shape(std::string&& name, Args&&... args) { - return std::make_shared>(std::string{solid.GetName()}, + return std::make_shared>(std::move(name), CR{std::forward(args)...}); } +//---------------------------------------------------------------------------// +/*! + * Construct a shape using the solid's name and forwarded arguments. + */ +template +auto make_shape(G4VSolid const& solid, Args&&... args) +{ + return make_shape(std::string{solid.GetName()}, + std::forward(args)...); +} + //---------------------------------------------------------------------------// /*! * Construct an ORANGE solid using the G4Solid's name and forwarded arguments. @@ -297,7 +323,7 @@ auto SolidConverter::cons(arg_type solid_base) -> result_type return make_solid(solid, Cylinder{outer_r[0], hh}, std::move(inner), - get_azimuthal_wedge(solid)); + make_wedge_azimuthal(solid)); } std::optional inner; @@ -307,7 +333,7 @@ auto SolidConverter::cons(arg_type solid_base) -> result_type } return make_solid( - solid, Cone{outer_r, hh}, std::move(inner), get_azimuthal_wedge(solid)); + solid, Cone{outer_r, hh}, std::move(inner), make_wedge_azimuthal(solid)); } //---------------------------------------------------------------------------// @@ -503,9 +529,8 @@ auto SolidConverter::polyhedra(arg_type solid_base) -> result_type auto const& solid = dynamic_cast(solid_base); auto const& params = *solid.GetOriginalParameters(); - // Opening angle: end - start phi - double const radius_factor - = std::cos(0.5 * params.Opening_angle / params.numSide); + // Convert from circumradius to apothem + double const radius_factor = std::cos(m_pi / params.numSide); std::vector zs(params.Num_z_planes); std::vector rmin(zs.size()); @@ -517,16 +542,14 @@ auto SolidConverter::polyhedra(arg_type solid_base) -> result_type rmax[i] = scale_(params.Rmax[i]) * radius_factor; } - auto startphi = native_value_to(solid.GetStartPhi()); - SolidEnclosedAngle angle( - startphi, native_value_to(solid.GetEndPhi()) - startphi); + auto angle = make_wedge_azimuthal_poly(solid); if (zs.size() == 2 && rmin[0] == rmin[1] && rmax[0] == rmax[1]) { // A solid prism double const hh = (zs[1] - zs[0]) / 2; double const orientation - = std::fmod(params.numSide * startphi.value(), real_type{1}); + = std::fmod(params.numSide * angle.start().value(), real_type{1}); if (rmin[0] != 0.0 || angle) { @@ -569,7 +592,7 @@ auto SolidConverter::sphere(arg_type solid_base) -> result_type inner = Sphere{scale_(inner_r)}; } - auto polar_wedge = get_polar_wedge(solid); + auto polar_wedge = make_wedge_polar(solid); if (!soft_equal(value_as(polar_wedge.interior()), 0.5)) { CELER_NOT_IMPLEMENTED("sphere with polar limits"); @@ -578,7 +601,7 @@ auto SolidConverter::sphere(arg_type solid_base) -> result_type return make_solid(solid, Sphere{scale_(solid.GetOuterRadius())}, std::move(inner), - get_azimuthal_wedge(solid)); + make_wedge_azimuthal(solid)); } //---------------------------------------------------------------------------// @@ -693,7 +716,7 @@ auto SolidConverter::tubs(arg_type solid_base) -> result_type return make_solid(solid, Cylinder{scale_(solid.GetOuterRadius()), hh}, std::move(inner), - get_azimuthal_wedge(solid)); + make_wedge_azimuthal(solid)); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/IntersectRegion.cc b/src/orange/orangeinp/IntersectRegion.cc index f86335e0a9..8376f6770b 100644 --- a/src/orange/orangeinp/IntersectRegion.cc +++ b/src/orange/orangeinp/IntersectRegion.cc @@ -441,6 +441,9 @@ void GenTrap::build(IntersectSurfaceBuilder& insert_surface) const insert_surface(Sense::outside, PlaneZ{-hz_}); insert_surface(Sense::inside, PlaneZ{hz_}); + // TODO: use plane normal equality from SoftSurfaceEqual + SoftEqual soft_equal{insert_surface.tol().rel}; + // Build the side planes for (auto i : range(lo_.size())) { diff --git a/src/orange/orangeinp/IntersectSurfaceBuilder.cc b/src/orange/orangeinp/IntersectSurfaceBuilder.cc index c2760289e4..1d8dc850ca 100644 --- a/src/orange/orangeinp/IntersectSurfaceBuilder.cc +++ b/src/orange/orangeinp/IntersectSurfaceBuilder.cc @@ -64,6 +64,15 @@ IntersectSurfaceBuilder::IntersectSurfaceBuilder(UnitBuilder* ub, State* state) state_->global_bzone.interior = ub->extents(); } +//---------------------------------------------------------------------------// +/*! + * Get the construction tolerance. + */ +auto IntersectSurfaceBuilder::tol() const -> Tol const& +{ + return ub_->tol(); +} + //---------------------------------------------------------------------------// /*! * Add a surface with a sense. diff --git a/src/orange/orangeinp/IntersectSurfaceBuilder.hh b/src/orange/orangeinp/IntersectSurfaceBuilder.hh index 8404a75ad7..19a771b39c 100644 --- a/src/orange/orangeinp/IntersectSurfaceBuilder.hh +++ b/src/orange/orangeinp/IntersectSurfaceBuilder.hh @@ -48,6 +48,15 @@ struct IntersectSurfaceState; class IntersectSurfaceBuilder { public: + //!@{ + //! \name Types + using Tol = Tolerance<>; + //!@} + + public: + // Get the construction tolerance + Tol const& tol() const; + // Add a surface with negative quadric value being "inside" template inline void operator()(S const& surf); diff --git a/src/orange/orangeinp/Solid.hh b/src/orange/orangeinp/Solid.hh index 6b8f8c30ac..3efd6ab775 100644 --- a/src/orange/orangeinp/Solid.hh +++ b/src/orange/orangeinp/Solid.hh @@ -93,7 +93,7 @@ class SolidBase : public ObjectInterface //! Optional excluded region virtual IntersectRegionInterface const* excluded() const = 0; - //! Angular restriction to add + //! Optional azimuthal angular restriction virtual SolidEnclosedAngle enclosed_angle() const = 0; protected: diff --git a/src/orange/orangeinp/detail/VolumeBuilder.cc b/src/orange/orangeinp/detail/VolumeBuilder.cc index 7babb01567..3a89b0f15c 100644 --- a/src/orange/orangeinp/detail/VolumeBuilder.cc +++ b/src/orange/orangeinp/detail/VolumeBuilder.cc @@ -29,6 +29,15 @@ VolumeBuilder::VolumeBuilder(CsgUnitBuilder* ub) : ub_{ub} CELER_ENSURE(transforms_.size() == 1); } +//---------------------------------------------------------------------------// +/*! + * Get the construction tolerance. + */ +auto VolumeBuilder::tol() const -> Tol const& +{ + return ub_->tol(); +} + //---------------------------------------------------------------------------// /*! * Access the local-to-global transform during construction. diff --git a/src/orange/orangeinp/detail/VolumeBuilder.hh b/src/orange/orangeinp/detail/VolumeBuilder.hh index 281d6eb3ec..6a5696f186 100644 --- a/src/orange/orangeinp/detail/VolumeBuilder.hh +++ b/src/orange/orangeinp/detail/VolumeBuilder.hh @@ -42,6 +42,7 @@ class VolumeBuilder //!@{ //! \name Type aliases using Metadata = Label; + using Tol = Tolerance<>; //!@} public: @@ -50,6 +51,9 @@ class VolumeBuilder //// ACCESSORS //// + // Get the construction tolerance + Tol const& tol() const; + //!@{ //! Access the unit builder for construction CsgUnitBuilder const& unit_builder() const { return *ub_; } diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index dad89d25ee..8c3e7f0e0a 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -266,7 +266,6 @@ celeritas_add_test(grid/PolyEvaluator.test.cc) celeritas_add_test(grid/RangeCalculator.test.cc) celeritas_add_test(grid/ValueGridBuilder.test.cc) celeritas_add_test(grid/ValueGridInserter.test.cc) -celeritas_add_test(grid/VectorUtils.test.cc) celeritas_add_test(grid/XsCalculator.test.cc) #-----------------------------------------------------------------------------# diff --git a/test/celeritas/optical/Cerenkov.test.cc b/test/celeritas/optical/Cerenkov.test.cc index 475ba39843..379d603c43 100644 --- a/test/celeritas/optical/Cerenkov.test.cc +++ b/test/celeritas/optical/Cerenkov.test.cc @@ -11,6 +11,7 @@ #include "corecel/cont/Range.hh" #include "corecel/data/CollectionBuilder.hh" #include "corecel/data/CollectionStateStore.hh" +#include "corecel/grid/VectorUtils.hh" #include "corecel/math/Algorithms.hh" #include "corecel/math/ArrayOperators.hh" #include "corecel/math/ArrayUtils.hh" @@ -18,7 +19,6 @@ #include "geocel/UnitUtils.hh" #include "celeritas/Constants.hh" #include "celeritas/Units.hh" -#include "celeritas/grid/VectorUtils.hh" #include "celeritas/io/ImportOpticalMaterial.hh" #include "celeritas/optical/CerenkovDndxCalculator.hh" #include "celeritas/optical/CerenkovGenerator.hh" diff --git a/test/corecel/CMakeLists.txt b/test/corecel/CMakeLists.txt index d9fec0401e..705463d539 100644 --- a/test/corecel/CMakeLists.txt +++ b/test/corecel/CMakeLists.txt @@ -49,6 +49,7 @@ celeritas_add_test(grid/Interpolator.test.cc) celeritas_add_test(grid/NonuniformGrid.test.cc) celeritas_add_test(grid/TwodGridCalculator.test.cc) celeritas_add_test(grid/UniformGrid.test.cc) +celeritas_add_test(grid/VectorUtils.test.cc) # IO set(CELERITASTEST_PREFIX corecel/io) diff --git a/test/celeritas/grid/VectorUtils.test.cc b/test/corecel/grid/VectorUtils.test.cc similarity index 97% rename from test/celeritas/grid/VectorUtils.test.cc rename to test/corecel/grid/VectorUtils.test.cc index 78f19fbb62..70d3eac56a 100644 --- a/test/celeritas/grid/VectorUtils.test.cc +++ b/test/corecel/grid/VectorUtils.test.cc @@ -3,9 +3,9 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/grid/VectorUtils.test.cc +//! \file corecel/grid/VectorUtils.test.cc //---------------------------------------------------------------------------// -#include "celeritas/grid/VectorUtils.hh" +#include "corecel/grid/VectorUtils.hh" #include diff --git a/test/corecel/math/Algorithms.test.cc b/test/corecel/math/Algorithms.test.cc index 16595e7ec7..de6184758b 100644 --- a/test/corecel/math/Algorithms.test.cc +++ b/test/corecel/math/Algorithms.test.cc @@ -98,6 +98,30 @@ TEST(AlgorithmsTest, any_of) EXPECT_FALSE(any_of(std::begin(items) + 2, std::end(items), is_true)); } +TEST(AlgorithmsTest, all_adjacent) +{ + static int const incr[] = {0, 1, 3, 20, 200}; + static int const vee[] = {3, 2, 1, 2, 3}; + static int const nondecr[] = {1, 1, 2, 3, 5, 8}; + + // Empty and single-element ranges + EXPECT_TRUE(all_adjacent( + std::begin(incr), std::begin(incr), [](int, int) { return false; })); + EXPECT_TRUE(all_adjacent(std::begin(incr), + std::begin(incr) + 1, + [](int, int) { return false; })); + + auto lt = [](int a, int b) { return a < b; }; + auto le = [](int a, int b) { return a <= b; }; + EXPECT_TRUE(all_adjacent(std::begin(incr), std::end(incr), lt)); + EXPECT_FALSE(all_adjacent(std::begin(vee), std::end(vee), lt)); + EXPECT_FALSE(all_adjacent(std::begin(nondecr), std::end(nondecr), lt)); + + EXPECT_TRUE(all_adjacent(std::begin(incr), std::end(incr), le)); + EXPECT_FALSE(all_adjacent(std::begin(vee), std::end(vee), le)); + EXPECT_TRUE(all_adjacent(std::begin(nondecr), std::end(nondecr), le)); +} + TEST(AlgorithmsTest, clamp) { EXPECT_EQ(123, clamp(123, 100, 200)); From 65ed16ebac77063635d512149fdb955461972e27 Mon Sep 17 00:00:00 2001 From: Amanda Lund Date: Thu, 23 May 2024 01:59:00 -0500 Subject: [PATCH 57/59] Support combined single and multiple Coulomb scattering (#1230) * Start adding support for combined single and multiple Coulomb scattering * Import polar angle limit and check for combined SS and MSC * Move material properties to material data and separate common SS and MSC data * Refactor Coulomb scattering data - Add a new data/params class for Wentzel OK&VI data that will be used by both the single Coulomb scattering model and the Wentzel VI MSC model - Import MscThetaLimit from EM parameters instead of the model PolarAngleLimit * Update material test * Update Wentzel distribution to support arbitrary cos theta min/max * Check kin factor and cross sections in Wentzel helper test * Defer WentzelVI cross section calculator * Get mott factor from helper, finish importing angle limit factor, and clean up code * Import nuclear form factor model from Geant4 * a_sq_factor can be zero with float * Change (1 - generate_canonical) --> generate_canonical in Wentzel distribution and update tests --- app/celer-sim/Runner.cc | 4 + src/accel/SharedParams.cc | 5 + src/celeritas/CMakeLists.txt | 1 + src/celeritas/Types.cc | 15 + src/celeritas/Types.hh | 14 + .../data/{MscData.hh => CommonCoulombData.hh} | 4 +- .../em/data/CoulombScatteringData.hh | 107 +- src/celeritas/em/data/UrbanMscData.hh | 4 +- src/celeritas/em/data/WentzelOKVIData.hh | 123 +++ src/celeritas/em/data/WentzelVIMscData.hh | 6 +- .../em/distribution/WentzelDistribution.hh | 107 +- .../em/executor/CoulombScatteringExecutor.hh | 6 +- .../interactor/CoulombScatteringInteractor.hh | 41 +- .../em/model/CoulombScatteringModel.cc | 887 +--------------- .../em/model/CoulombScatteringModel.cu | 6 +- .../em/model/CoulombScatteringModel.hh | 41 +- src/celeritas/em/params/UrbanMscParams.cc | 5 +- src/celeritas/em/params/WentzelOKVIParams.cc | 951 ++++++++++++++++++ src/celeritas/em/params/WentzelOKVIParams.hh | 85 ++ src/celeritas/em/params/WentzelVIMscParams.cc | 5 +- .../em/params/detail/MscParamsHelper.cc | 2 +- .../em/params/detail/MscParamsHelper.hh | 4 +- .../em/process/CoulombScatteringProcess.cc | 13 +- .../em/process/CoulombScatteringProcess.hh | 14 +- src/celeritas/em/xs/MottRatioCalculator.hh | 17 +- src/celeritas/em/xs/WentzelHelper.hh | 194 +++- .../em/xs/WentzelTransportXsCalculator.hh | 61 +- src/celeritas/ext/GeantImporter.cc | 26 + src/celeritas/ext/GeantPhysicsOptions.hh | 10 + .../ext/GeantPhysicsOptionsIO.json.cc | 19 + .../ext/detail/CelerEmStandardPhysics.cc | 25 + src/celeritas/global/CoreParams.cc | 5 + src/celeritas/global/CoreParams.hh | 6 +- src/celeritas/global/CoreTrackData.hh | 3 + src/celeritas/io/ImportData.cc | 33 + src/celeritas/io/ImportData.hh | 6 + src/celeritas/io/ImportParameters.hh | 11 +- src/celeritas/phys/ProcessBuilder.cc | 6 +- src/celeritas/phys/ProcessBuilder.hh | 1 - test/celeritas/GlobalTestBase.cc | 1 + test/celeritas/GlobalTestBase.hh | 18 + test/celeritas/ImportedDataTestBase.cc | 8 + test/celeritas/ImportedDataTestBase.hh | 1 + test/celeritas/MockTestBase.hh | 1 + test/celeritas/OnlyGeoTestBase.hh | 1 + test/celeritas/SimpleTestBase.cc | 8 + test/celeritas/SimpleTestBase.hh | 1 + test/celeritas/em/CoulombScattering.test.cc | 297 +++--- test/celeritas/ext/GeantImporter.test.cc | 7 +- test/celeritas/mat/Material.test.cc | 20 +- 50 files changed, 1921 insertions(+), 1315 deletions(-) rename src/celeritas/em/data/{MscData.hh => CommonCoulombData.hh} (93%) create mode 100644 src/celeritas/em/data/WentzelOKVIData.hh create mode 100644 src/celeritas/em/params/WentzelOKVIParams.cc create mode 100644 src/celeritas/em/params/WentzelOKVIParams.hh diff --git a/app/celer-sim/Runner.cc b/app/celer-sim/Runner.cc index 8e0e37ffab..2d480af887 100644 --- a/app/celer-sim/Runner.cc +++ b/app/celer-sim/Runner.cc @@ -30,6 +30,7 @@ #include "celeritas/Types.hh" #include "celeritas/Units.hh" #include "celeritas/em/params/UrbanMscParams.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/ext/GeantImporter.hh" #include "celeritas/ext/GeantSetup.hh" #include "celeritas/ext/RootFileManager.hh" @@ -308,6 +309,9 @@ void Runner::build_core_params(RunnerInput const& inp, params.cutoff = CutoffParams::from_import( imported, params.particle, params.material); + // Construct shared data for Coulomb scattering + params.wentzel = WentzelOKVIParams::from_import(imported, params.material); + // Load physics: create individual processes with make_shared params.physics = [¶ms, &inp, &imported] { PhysicsParams::Input input; diff --git a/src/accel/SharedParams.cc b/src/accel/SharedParams.cc index 0a882de325..ea6483fd4f 100644 --- a/src/accel/SharedParams.cc +++ b/src/accel/SharedParams.cc @@ -36,6 +36,7 @@ #include "geocel/GeantUtils.hh" #include "geocel/g4/GeantGeoParams.hh" #include "celeritas/Types.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/ext/GeantImporter.hh" #include "celeritas/ext/RootExporter.hh" #include "celeritas/geo/GeoMaterialParams.hh" @@ -58,6 +59,7 @@ #include "AlongStepFactory.hh" #include "SetupOptions.hh" + #include "detail/HitManager.hh" #include "detail/OffloadWriter.hh" @@ -530,6 +532,9 @@ void SharedParams::initialize_core(SetupOptions const& options) params.cutoff = CutoffParams::from_import( *imported, params.particle, params.material); + // Construct shared data for Coulomb scattering + params.wentzel = WentzelOKVIParams::from_import(*imported, params.material); + // Load physics: create individual processes with make_shared params.physics = [¶ms, &options, &imported] { PhysicsParams::Input input; diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 87710fdf05..04084ddd52 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND SOURCES em/params/AtomicRelaxationParams.cc em/params/FluctuationParams.cc em/params/UrbanMscParams.cc + em/params/WentzelOKVIParams.cc em/params/WentzelVIMscParams.cc em/params/detail/MscParamsHelper.cc em/process/BremsstrahlungProcess.cc diff --git a/src/celeritas/Types.cc b/src/celeritas/Types.cc index a1616dcb39..398256a802 100644 --- a/src/celeritas/Types.cc +++ b/src/celeritas/Types.cc @@ -93,6 +93,21 @@ char const* to_cstring(MscStepLimitAlgorithm value) return to_cstring_impl(value); } +//---------------------------------------------------------------------------// +/*! + * Get a string corresponding to the nuclear form factor model. + */ +char const* to_cstring(NuclearFormFactorType value) +{ + static EnumStringMapper const to_cstring_impl{ + "none", + "flat", + "exponential", + "gaussian", + }; + return to_cstring_impl(value); +} + //---------------------------------------------------------------------------// /*! * Checks that the TrackOrder will sort tracks by actions applied at the given diff --git a/src/celeritas/Types.hh b/src/celeritas/Types.hh index 9b154de233..8fb6377dea 100644 --- a/src/celeritas/Types.hh +++ b/src/celeritas/Types.hh @@ -160,6 +160,17 @@ enum class MscStepLimitAlgorithm size_, }; +//---------------------------------------------------------------------------// +//! Nuclear form factor model for Coulomb scattering +enum class NuclearFormFactorType +{ + none, + flat, + exponential, + gaussian, + size_ +}; + //---------------------------------------------------------------------------// // HELPER STRUCTS //---------------------------------------------------------------------------// @@ -196,6 +207,9 @@ char const* to_cstring(TrackOrder); // Get a string corresponding to the MSC step limit algorithm char const* to_cstring(MscStepLimitAlgorithm value); +// Get a string corresponding to the nuclear form factor model +char const* to_cstring(NuclearFormFactorType value); + // Checks that the TrackOrder will sort tracks by actions applied at the given // ActionOrder bool is_action_sorted(ActionOrder action, TrackOrder track); diff --git a/src/celeritas/em/data/MscData.hh b/src/celeritas/em/data/CommonCoulombData.hh similarity index 93% rename from src/celeritas/em/data/MscData.hh rename to src/celeritas/em/data/CommonCoulombData.hh index bab60d6930..e8ae40612f 100644 --- a/src/celeritas/em/data/MscData.hh +++ b/src/celeritas/em/data/CommonCoulombData.hh @@ -3,7 +3,7 @@ // See the top-level COPYRIGHT file for details. // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/em/data/MscData.hh +//! \file celeritas/em/data/CommonCoulombData.hh //---------------------------------------------------------------------------// #pragma once @@ -19,7 +19,7 @@ namespace celeritas * * TODO these will probably be changed to a map over all particle IDs. */ -struct MscIds +struct CoulombIds { ParticleId electron; ParticleId positron; diff --git a/src/celeritas/em/data/CoulombScatteringData.hh b/src/celeritas/em/data/CoulombScatteringData.hh index 5c068beda7..220fea4de1 100644 --- a/src/celeritas/em/data/CoulombScatteringData.hh +++ b/src/celeritas/em/data/CoulombScatteringData.hh @@ -9,120 +9,31 @@ #include "corecel/Macros.hh" #include "corecel/Types.hh" -#include "corecel/data/Collection.hh" #include "celeritas/Quantities.hh" #include "celeritas/Types.hh" -namespace celeritas -{ -//---------------------------------------------------------------------------// -/*! - * Particle and action ids used by CoulombScatteringModel. - */ -struct CoulombScatteringIds -{ - ActionId action; - ParticleId electron; - ParticleId positron; - ParticleId proton; - - explicit CELER_FUNCTION operator bool() const - { - // TODO: enable when protons are supported - return action && electron && positron /* && proton */; - } -}; - -//---------------------------------------------------------------------------// -/*! - * Per-element data used by the CoulombScatteringModel. - * - * The matrix of coefficients used to approximate the ratio of the Mott to - * Rutherford cross sections was developed in - * T. Lijian, H. Quing and L. Zhengming, Radiat. Phys. Chem. 45 (1995), - * 235-245 - * and - * M. J. Boschini et al. arXiv:1111.4042 - */ -struct CoulombScatteringElementData -{ - //!@{ - //! \name Dimensions for Mott coefficient matrices - static constexpr size_type num_mott_beta_bins = 6; - static constexpr size_type num_mott_theta_bins = 5; - static constexpr size_type num_mott_elements = 92; - //!@} - - using BetaArray = Array; - using ThetaArray = Array; - using MottCoeffMatrix = Array; +#include "CommonCoulombData.hh" - //! Matrix of Mott coefficients [theta][beta] - MottCoeffMatrix mott_coeff; -}; - -//---------------------------------------------------------------------------// -/*! - * Supported models of nuclear form factors. - */ -enum class NuclearFormFactorType +namespace celeritas { - none, - flat, - exponential, - gaussian -}; - //---------------------------------------------------------------------------// /*! * Constant shared data used by the CoulombScatteringModel. */ -template struct CoulombScatteringData { - template - using ElementItems = celeritas::Collection; - template - using IsotopeItems = celeritas::Collection; - - // Ids - CoulombScatteringIds ids; - - //! Constant prefactor for the squared momentum transfer [(MeV/c)^-2] - IsotopeItems nuclear_form_prefactor; - - // Per element form factors - ElementItems elem_data; + // Particle IDs + CoulombIds ids; - // User-defined factor for the screening coefficient - real_type screening_factor; + // Action ID + ActionId action; - // Model for the form factor to use - NuclearFormFactorType form_factor_type; + //! Cosine of the maximum scattering polar angle + static CELER_CONSTEXPR_FUNCTION real_type cos_thetamax() { return -1; } // Check if the data is initialized - explicit CELER_FUNCTION operator bool() const - { - return ids && !nuclear_form_prefactor.empty() && !elem_data.empty(); - } - - // Copy initialize from an existing CoulombScatteringData - template - CoulombScatteringData& operator=(CoulombScatteringData const& other) - { - CELER_EXPECT(other); - ids = other.ids; - nuclear_form_prefactor = other.nuclear_form_prefactor; - elem_data = other.elem_data; - form_factor_type = other.form_factor_type; - screening_factor = other.screening_factor; - return *this; - } + explicit CELER_FUNCTION operator bool() const { return ids && action; } }; -using CoulombScatteringDeviceRef = DeviceCRef; -using CoulombScatteringHostRef = HostCRef; -using CoulombScatteringRef = NativeCRef; - //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/em/data/UrbanMscData.hh b/src/celeritas/em/data/UrbanMscData.hh index 1c28a90aaf..1e6873fb38 100644 --- a/src/celeritas/em/data/UrbanMscData.hh +++ b/src/celeritas/em/data/UrbanMscData.hh @@ -14,7 +14,7 @@ #include "celeritas/Types.hh" #include "celeritas/grid/XsGridData.hh" -#include "MscData.hh" +#include "CommonCoulombData.hh" namespace celeritas { @@ -137,7 +137,7 @@ struct UrbanMscData //// DATA //// //! Particle IDs - MscIds ids; + CoulombIds ids; //! Mass of of electron in MeV units::MevMass electron_mass; //! User-assignable options diff --git a/src/celeritas/em/data/WentzelOKVIData.hh b/src/celeritas/em/data/WentzelOKVIData.hh new file mode 100644 index 0000000000..3a87c88629 --- /dev/null +++ b/src/celeritas/em/data/WentzelOKVIData.hh @@ -0,0 +1,123 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/em/data/WentzelOKVIData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/Types.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/Constants.hh" +#include "celeritas/Quantities.hh" +#include "celeritas/Types.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Parameters used in both single Coulomb scattering and Wentzel VI MSC models. + * + * When the single Coulomb scattering and Wentzel VI MSC models are used + * together, the MSC model is used to sample scatterings with angles below the + * polar angle limit, and the single scattering model is used for angles above + * the limit. + */ +struct CoulombParameters +{ + //! Whether to use combined single and multiple scattering + bool is_combined{true}; + //! Polar angle limit between single and multiple scattering + real_type costheta_limit{-1}; + //! Factor for the screening coefficient + real_type screening_factor{1}; + //! Factor used to calculate the maximum scattering angle off of a nucleus + real_type a_sq_factor{0.5 + * ipow<2>(constants::hbar_planck * constants::c_light + * units::femtometer)}; + // Model for the form factor to use + NuclearFormFactorType form_factor_type{NuclearFormFactorType::exponential}; + + explicit CELER_FUNCTION operator bool() const + { + return costheta_limit >= -1 && costheta_limit <= 1 + && screening_factor > 0 && a_sq_factor >= 0 + && form_factor_type != NuclearFormFactorType::size_; + } +}; + +//---------------------------------------------------------------------------// +/*! + * Per-element data used by the Coulomb scattering and Wentzel VI models. + * + * The matrix of coefficients used to approximate the ratio of the Mott to + * Rutherford cross sections was developed in T. Lijian, H. Quing and L. + * Zhengming, Radiat. Phys. Chem. 45 (1995), 235-245 and M. J. Boschini et al. + * arXiv:1111.4042 + */ +struct MottElementData +{ + //!@{ + //! \name Dimensions for Mott coefficient matrices + static constexpr size_type num_mott_beta_bins = 6; + static constexpr size_type num_mott_theta_bins = 5; + static constexpr size_type num_mott_elements = 92; + //!@} + + using BetaArray = Array; + using ThetaArray = Array; + using MottCoeffMatrix = Array; + + //! Matrix of Mott coefficients [theta][beta] + MottCoeffMatrix mott_coeff; +}; + +//---------------------------------------------------------------------------// +/*! + * Constant shared data used by the Coulomb scattering and Wentzel VI models. + */ +template +struct WentzelOKVIData +{ + template + using ElementItems = celeritas::Collection; + template + using IsotopeItems = celeritas::Collection; + template + using MaterialItems = Collection; + + // User-assignable parameters + CoulombParameters params; + + // Constant prefactor for the squared momentum transfer [(MeV/c)^-2] + IsotopeItems nuclear_form_prefactor; + + // Per element form factors + ElementItems elem_data; + + // Inverse effective A^2/3 [1/mass^2/3] + MaterialItems inv_mass_cbrt_sq; + + // Check if the data is initialized + explicit CELER_FUNCTION operator bool() const + { + return params && !elem_data.empty() + && params.is_combined == !inv_mass_cbrt_sq.empty(); + } + + template + WentzelOKVIData& operator=(WentzelOKVIData const& other) + { + CELER_EXPECT(other); + params = other.params; + nuclear_form_prefactor = other.nuclear_form_prefactor; + elem_data = other.elem_data; + inv_mass_cbrt_sq = other.inv_mass_cbrt_sq; + return *this; + } +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/em/data/WentzelVIMscData.hh b/src/celeritas/em/data/WentzelVIMscData.hh index 0424b8559b..24a54b5a9f 100644 --- a/src/celeritas/em/data/WentzelVIMscData.hh +++ b/src/celeritas/em/data/WentzelVIMscData.hh @@ -14,7 +14,7 @@ #include "celeritas/Types.hh" #include "celeritas/grid/XsGridData.hh" -#include "MscData.hh" +#include "CommonCoulombData.hh" namespace celeritas { @@ -26,7 +26,7 @@ struct WentzelVIMscParameters { using Energy = units::MevEnergy; - real_type single_scattering_fact{1.25}; //!< single scattering factor + real_type single_scattering_factor{1.25}; Energy low_energy_limit{0}; Energy high_energy_limit{0}; @@ -52,7 +52,7 @@ struct WentzelVIMscData //// DATA //// //! Particle IDs - MscIds ids; + CoulombIds ids; //! Mass of of electron in MeV units::MevMass electron_mass; //! User-assignable options diff --git a/src/celeritas/em/distribution/WentzelDistribution.hh b/src/celeritas/em/distribution/WentzelDistribution.hh index fa9dcda109..0c95d80d15 100644 --- a/src/celeritas/em/distribution/WentzelDistribution.hh +++ b/src/celeritas/em/distribution/WentzelDistribution.hh @@ -12,7 +12,6 @@ #include "corecel/Macros.hh" #include "corecel/Types.hh" #include "corecel/math/Algorithms.hh" -#include "celeritas/em/data/CoulombScatteringData.hh" #include "celeritas/em/interactor/detail/PhysicsConstants.hh" #include "celeritas/em/xs/MottRatioCalculator.hh" #include "celeritas/em/xs/WentzelHelper.hh" @@ -53,11 +52,13 @@ class WentzelDistribution public: // Construct with state and model data inline CELER_FUNCTION - WentzelDistribution(ParticleTrackView const& particle, + WentzelDistribution(NativeCRef const& wentzel, + WentzelHelper const& helper, + ParticleTrackView const& particle, IsotopeView const& target, - CoulombScatteringElementData const& element_data, - Energy cutoff, - CoulombScatteringRef const& data); + ElementId el_id, + real_type cos_thetamin, + real_type cos_thetamax); // Sample the polar scattering angle template @@ -66,8 +67,11 @@ class WentzelDistribution private: //// DATA //// - // Shared model data - CoulombScatteringRef const& data_; + // Shared Coulomb scattering data + NativeCRef const& wentzel_; + + // Helper for calculating xs ratio and other quantities + WentzelHelper const& helper_; // Incident particle ParticleTrackView const& particle_; @@ -75,11 +79,16 @@ class WentzelDistribution // Target isotope IsotopeView const& target_; - // Mott coefficients for the target element - CoulombScatteringElementData const& element_data_; + // Target element + ElementId el_id_; - // Helper for calculating xs ratio and other quantities - WentzelHelper const helper_; + // Cosine of the minimum scattering angle + real_type cos_thetamin_; + + // Cosine of the maximum scattering angle + real_type cos_thetamax_; + + //// HELPER FUNCTIONS //// // Calculates the form factor from the scattered polar angle inline CELER_FUNCTION real_type calculate_form_factor(real_type cos_t) const; @@ -95,8 +104,9 @@ class WentzelDistribution // Sample the scattered polar angle template - inline CELER_FUNCTION real_type sample_cos_t(real_type cos_t_max, - Engine& rng) const; + inline CELER_FUNCTION real_type sample_costheta(real_type cos_thetamin, + real_type cos_thetamax, + Engine& rng) const; // Helper function for calculating the flat form factor inline static CELER_FUNCTION real_type flat_form_factor(real_type x); @@ -113,17 +123,25 @@ class WentzelDistribution */ CELER_FUNCTION WentzelDistribution::WentzelDistribution( + NativeCRef const& wentzel, + WentzelHelper const& helper, ParticleTrackView const& particle, IsotopeView const& target, - CoulombScatteringElementData const& element_data, - Energy cutoff, - CoulombScatteringRef const& data) - : data_(data) + ElementId el_id, + real_type cos_thetamin, + real_type cos_thetamax) + : wentzel_(wentzel) + , helper_(helper) , particle_(particle) , target_(target) - , element_data_(element_data) - , helper_(particle, target.atomic_number(), data, cutoff) + , el_id_(el_id) + , cos_thetamin_(cos_thetamin) + , cos_thetamax_(cos_thetamax) { + CELER_EXPECT(el_id_ < wentzel_.elem_data.size()); + CELER_EXPECT(cos_thetamin_ >= -1 && cos_thetamin_ <= 1); + CELER_EXPECT(cos_thetamax_ >= -1 && cos_thetamax_ <= 1); + CELER_EXPECT(cos_thetamax_ <= cos_thetamin_); } //---------------------------------------------------------------------------// @@ -134,25 +152,28 @@ template CELER_FUNCTION real_type WentzelDistribution::operator()(Engine& rng) const { real_type cos_theta = 1; - if (BernoulliDistribution(helper_.calc_xs_ratio())(rng)) + if (BernoulliDistribution( + helper_.calc_xs_ratio(cos_thetamin_, cos_thetamax_))(rng)) { // Scattered off of electrons - cos_theta = this->sample_cos_t(helper_.costheta_max_electron(), rng); + real_type const cos_thetamax_elec = helper_.cos_thetamax_electron(); + real_type cos_thetamin = max(cos_thetamin_, cos_thetamax_elec); + real_type cos_thetamax = max(cos_thetamax_, cos_thetamax_elec); + CELER_ASSERT(cos_thetamin > cos_thetamax); + cos_theta = this->sample_costheta(cos_thetamin, cos_thetamax, rng); } else { // Scattered off of nucleus - cos_theta = this->sample_cos_t(-1, rng); + cos_theta = this->sample_costheta(cos_thetamin_, cos_thetamax_, rng); // Calculate rejection for fake scattering // TODO: Reference? - real_type mott_coeff - = 1 + real_type(2e-4) * ipow<2>(target_.atomic_number().get()); - MottRatioCalculator mott_xsec(element_data_, + MottRatioCalculator mott_xsec(wentzel_.elem_data[el_id_], std::sqrt(particle_.beta_sq())); real_type g_rej = mott_xsec(cos_theta) * ipow<2>(this->calculate_form_factor(cos_theta)) - / mott_coeff; + / helper_.mott_factor(); if (!BernoulliDistribution(g_rej)(rng)) { @@ -160,7 +181,6 @@ CELER_FUNCTION real_type WentzelDistribution::operator()(Engine& rng) const cos_theta = 1; } } - return cos_theta; } @@ -179,7 +199,7 @@ CELER_FUNCTION real_type WentzelDistribution::calculate_form_factor(real_type cos_t) const { real_type mt_sq = this->mom_transfer_sq(cos_t); - switch (data_.form_factor_type) + switch (wentzel_.params.form_factor_type) { case NuclearFormFactorType::none: return 1; @@ -194,8 +214,9 @@ WentzelDistribution::calculate_form_factor(real_type cos_t) const return 1 / ipow<2>(1 + this->nuclear_form_prefactor() * mt_sq); case NuclearFormFactorType::gaussian: return std::exp(-2 * this->nuclear_form_prefactor() * mt_sq); + default: + CELER_ASSERT_UNREACHABLE(); } - CELER_ASSERT_UNREACHABLE(); } //---------------------------------------------------------------------------// @@ -241,28 +262,36 @@ CELER_FUNCTION real_type WentzelDistribution::mom_transfer_sq(real_type cos_t) c */ CELER_FUNCTION real_type WentzelDistribution::nuclear_form_prefactor() const { - return data_.nuclear_form_prefactor[target_.isotope_id()]; + CELER_EXPECT(target_.isotope_id() < wentzel_.nuclear_form_prefactor.size()); + return wentzel_.nuclear_form_prefactor[target_.isotope_id()]; } //---------------------------------------------------------------------------// /*! * Sample the scattering polar angle of the incident particle. * - * The probability is given in [Fern] - * eqn 88 and is nomalized on the interval - * \f$ cos\theta \in [1, \cos\theta_{max}] \f$. + * The probability is given in [Fern] eqn 88 and is nomalized on the interval + * \f$ cos\theta \in [\cos\theta_{min}, \cos\theta_{max}] \f$. The sampling + * function for \f$ \mu = \frac{1}{2}(1 - \cos\theta) \f$ is + * \f[ + \mu = \mu_1 + \frac{(A + \mu_1) \xi (\mu_2 - \mu_1)}{A + \mu_2 - \xi (\mu_2 + - \mu_1)}, + * \f] + * where \f$ \mu_1 = \frac{1}{2}(1 - \cos\theta_{min}) \f$, \f$ \mu_2 = + * \frac{1}{2}(1 - \cos\theta_{max}) \f$, \f$ A \f$ is the screening + * coefficient, and \f$ \xi \sim U(0,1) \f$. */ template -CELER_FUNCTION real_type WentzelDistribution::sample_cos_t(real_type cos_t_max, - Engine& rng) const +CELER_FUNCTION real_type WentzelDistribution::sample_costheta( + real_type cos_thetamin, real_type cos_thetamax, Engine& rng) const { // Sample scattering angle [Fern] eqn 92, where cos(theta) = 1 - 2*mu - // For incident electrons / positrons, theta_min = 0 always - real_type const mu = real_type{0.5} * (1 - cos_t_max); - real_type const xi = generate_canonical(rng); + real_type const mu1 = real_type{0.5} * (1 - cos_thetamin); + real_type const mu2 = real_type{0.5} * (1 - cos_thetamax); + real_type const w = generate_canonical(rng) * (mu2 - mu1); real_type const sc = helper_.screening_coefficient(); - real_type result = 1 - 2 * sc * mu * (1 - xi) / (sc + mu * xi); + real_type result = 1 - 2 * mu1 - 2 * (sc + mu1) * w / (sc + mu2 - w); CELER_ENSURE(result >= -1 && result <= 1); return result; } diff --git a/src/celeritas/em/executor/CoulombScatteringExecutor.hh b/src/celeritas/em/executor/CoulombScatteringExecutor.hh index 85895ea6fc..81b3f9b6e0 100644 --- a/src/celeritas/em/executor/CoulombScatteringExecutor.hh +++ b/src/celeritas/em/executor/CoulombScatteringExecutor.hh @@ -10,6 +10,7 @@ #include "corecel/Assert.hh" #include "corecel/Macros.hh" #include "celeritas/em/data/CoulombScatteringData.hh" +#include "celeritas/em/data/WentzelOKVIData.hh" #include "celeritas/em/interactor/CoulombScatteringInteractor.hh" #include "celeritas/global/CoreTrackView.hh" #include "celeritas/mat/IsotopeSelector.hh" @@ -27,7 +28,8 @@ struct CoulombScatteringExecutor inline CELER_FUNCTION Interaction operator()(celeritas::CoreTrackView const& track); - CoulombScatteringRef params; + CoulombScatteringData params; + NativeCRef wentzel; }; //---------------------------------------------------------------------------// @@ -56,7 +58,7 @@ CoulombScatteringExecutor::operator()(CoreTrackView const& track) // Construct the interactor CoulombScatteringInteractor interact( - params, particle, dir, target, element_id, cutoffs); + params, wentzel, particle, dir, material, target, element_id, cutoffs); // Execute the interactor return interact(rng); diff --git a/src/celeritas/em/interactor/CoulombScatteringInteractor.hh b/src/celeritas/em/interactor/CoulombScatteringInteractor.hh index 6b7cc9d582..89929a9ea9 100644 --- a/src/celeritas/em/interactor/CoulombScatteringInteractor.hh +++ b/src/celeritas/em/interactor/CoulombScatteringInteractor.hh @@ -13,6 +13,7 @@ #include "celeritas/Quantities.hh" #include "celeritas/Types.hh" #include "celeritas/em/data/CoulombScatteringData.hh" +#include "celeritas/em/data/WentzelOKVIData.hh" #include "celeritas/em/distribution/WentzelDistribution.hh" #include "celeritas/mat/ElementView.hh" #include "celeritas/mat/MaterialView.hh" @@ -52,11 +53,13 @@ class CoulombScatteringInteractor public: //! Construct with shared and state data inline CELER_FUNCTION - CoulombScatteringInteractor(CoulombScatteringRef const& shared, + CoulombScatteringInteractor(CoulombScatteringData const& shared, + NativeCRef const& wentzel, ParticleTrackView const& particle, Real3 const& inc_direction, + MaterialView const& material, IsotopeView const& target, - ElementId const& el_id, + ElementId el_id, CutoffView const& cutoffs); //! Sample an interaction with the given RNG @@ -75,8 +78,11 @@ class CoulombScatteringInteractor // Target isotope IsotopeView const& target_; + // Helper for calculating xs ratio and other quantities + WentzelHelper const helper_; + // Scattering direction distribution of the Wentzel model - WentzelDistribution const sample_angle; + WentzelDistribution const sample_angle_; //// HELPER FUNCTIONS //// @@ -92,21 +98,32 @@ class CoulombScatteringInteractor */ CELER_FUNCTION CoulombScatteringInteractor::CoulombScatteringInteractor( - CoulombScatteringRef const& shared, + CoulombScatteringData const& shared, + NativeCRef const& wentzel, ParticleTrackView const& particle, Real3 const& inc_direction, + MaterialView const& material, IsotopeView const& target, - ElementId const& el_id, + ElementId el_id, CutoffView const& cutoffs) : inc_direction_(inc_direction) , particle_(particle) , target_(target) - , sample_angle(particle, - target, - shared.elem_data[el_id], - // TODO: Use proton when supported - cutoffs.energy(shared.ids.electron), - shared) + , helper_(particle, + material, + target.atomic_number(), + wentzel, + shared.ids, + // TODO: Use the proton production cutoff when the recoiled + // nucleus production is supported + cutoffs.energy(shared.ids.electron)) + , sample_angle_(wentzel, + helper_, + particle, + target, + el_id, + helper_.cos_thetamax_nuclear(), + shared.cos_thetamax()) { CELER_EXPECT(particle_.particle_id() == shared.ids.electron || particle_.particle_id() == shared.ids.positron); @@ -125,7 +142,7 @@ CELER_FUNCTION Interaction CoulombScatteringInteractor::operator()(Engine& rng) Interaction result; // Sample the new direction - real_type cos_theta = sample_angle(rng); + real_type cos_theta = sample_angle_(rng); UniformRealDistribution sample_phi(0, 2 * constants::pi); result.direction = rotate(inc_direction_, from_spherical(cos_theta, sample_phi(rng))); diff --git a/src/celeritas/em/model/CoulombScatteringModel.cc b/src/celeritas/em/model/CoulombScatteringModel.cc index 59d2219072..9da149e6dc 100644 --- a/src/celeritas/em/model/CoulombScatteringModel.cc +++ b/src/celeritas/em/model/CoulombScatteringModel.cc @@ -7,35 +7,32 @@ //---------------------------------------------------------------------------// #include "CoulombScatteringModel.hh" +#include + #include "celeritas_config.h" -#include "corecel/sys/ScopedMem.hh" +#include "celeritas/Constants.hh" #include "celeritas/Units.hh" #include "celeritas/em/data/CoulombScatteringData.hh" #include "celeritas/em/executor/CoulombScatteringExecutor.hh" #include "celeritas/em/interactor/detail/PhysicsConstants.hh" -#include "celeritas/em/process/CoulombScatteringProcess.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/global/ActionLauncher.hh" #include "celeritas/global/CoreParams.hh" #include "celeritas/global/TrackExecutor.hh" #include "celeritas/io/ImportParameters.hh" #include "celeritas/io/ImportProcess.hh" -#include "celeritas/mat/ElementView.hh" -#include "celeritas/mat/MaterialParams.hh" #include "celeritas/phys/InteractionApplier.hh" #include "celeritas/phys/PDGNumber.hh" #include "celeritas/phys/ParticleParams.hh" -#include "celeritas/phys/ParticleView.hh" namespace celeritas { //---------------------------------------------------------------------------// /*! - * Construct from model ID, particle data, and material data. + * Construct from model ID and shared data. */ CoulombScatteringModel::CoulombScatteringModel(ActionId id, ParticleParams const& particles, - MaterialParams const& materials, - Options const& options, SPConstImported data) : imported_(data, particles, @@ -45,32 +42,16 @@ CoulombScatteringModel::CoulombScatteringModel(ActionId id, { CELER_EXPECT(id); - ScopedMem record_mem("CoulombScatteringModel.construct"); - HostVal host_data; - - // This is where the data is built and transfered to the device - host_data.ids.action = id; - host_data.ids.electron = particles.find(pdg::electron()); - host_data.ids.positron = particles.find(pdg::positron()); - host_data.ids.proton = particles.find(pdg::proton()); - - CELER_VALIDATE(host_data.ids, - << "missing IDs (required for " << this->description() - << ")"); - - // Select form factor - host_data.form_factor_type = options.form_factor_model; + data_.action = id; + data_.ids.electron = particles.find(pdg::electron()); + data_.ids.positron = particles.find(pdg::positron()); - // Pass user-defined screening factor - host_data.screening_factor = options.screening_factor; + CELER_VALIDATE(data_.ids, + << "missing electron and/or positron particles (required " + "for " + << this->description() << ")"); - // Load Mott coefficients - build_data(host_data, materials); - - // Construct data on device - data_ = CollectionMirror{std::move(host_data)}; - - CELER_ENSURE(this->data_); + CELER_ENSURE(data_); } //---------------------------------------------------------------------------// @@ -110,11 +91,14 @@ auto CoulombScatteringModel::micro_xs(Applicability applic) const void CoulombScatteringModel::execute(CoreParams const& params, CoreStateHost& state) const { + CELER_EXPECT(params.wentzel()); + auto execute = make_action_track_executor( params.ptr(), state.ptr(), this->action_id(), - InteractionApplier{CoulombScatteringExecutor{this->host_ref()}}); + InteractionApplier{CoulombScatteringExecutor{ + this->host_ref(), params.wentzel()->host_ref()}}); return launch_action(*this, params, state, execute); } @@ -133,842 +117,7 @@ void CoulombScatteringModel::execute(CoreParams const&, CoreStateDevice&) const */ ActionId CoulombScatteringModel::action_id() const { - return this->host_ref().ids.action; -} - -//---------------------------------------------------------------------------// -/*! - * Load Mott coefficients and construct per-element data. - */ -void CoulombScatteringModel::build_data( - HostVal& host_data, MaterialParams const& materials) -{ - // Build element data - size_type const num_elements = materials.num_elements(); - auto elem_data = make_builder(&host_data.elem_data); - elem_data.reserve(num_elements); - - for (auto el_id : range(ElementId{num_elements})) - { - // Load Mott coefficients - CoulombScatteringElementData z_data; - z_data.mott_coeff - = get_mott_coeff_matrix(materials.get(el_id).atomic_number()); - elem_data.push_back(z_data); - } - - auto prefactors = make_builder(&host_data.nuclear_form_prefactor); - prefactors.reserve(materials.num_isotopes()); - for (auto iso_id : range(IsotopeId{materials.num_isotopes()})) - { - prefactors.push_back( - this->calc_nuclear_form_prefactor(materials.get(iso_id))); - } -} - -//---------------------------------------------------------------------------// -/*! - * Get interpolated Mott coefficients for an element. - * - * These coefficients are used by the Lijian, Quing, Zhengming - * expression [PRM 8.48] for atomic numbers 1 <= Z <= 92. - * This data was taken from Geant4's G4MottData.hh file. - * - * For higher Z values, the PRM cites a numerical solution by Boschini - * et al (2013), but does not implement it. - */ -CoulombScatteringElementData::MottCoeffMatrix -CoulombScatteringModel::get_mott_coeff_matrix(AtomicNumber z) -{ - CELER_EXPECT(z); - - using CoeffMat = CoulombScatteringElementData::MottCoeffMatrix; - // clang-format off - static CoeffMat const mott_coeffs[] = { - //H....................................... - CoeffMat{{ - {1.e0,2.67363e-8,7.1153e-8,-9.7703e-8,-6.69132e-7,-3.09263e-7}, - {1.17182e-2,1.62222e-2,-5.90397e-5,-1.05585e-4,4.17873e-4,9.13843e-4}, - {-2.65955e-1,-7.29531e-1,-4.99796e-1,2.83507e-4,-9.09042e-4,-2.20244e-3}, - {-1.82348e-4,-8.86355e-5,-1.90554e-4,-2.49708e-4,6.35004e-4,1.73523e-3}, - {4.70966e-5,-4.09705e-6,3.75218e-5,8.05645e-5,-1.90534e-4,-5.42847e-4} - }}, - //He....................................... - CoeffMat{{ - {1.e0,3.76476e-8,-3.05313e-7,-3.27422e-7,2.44235e-6,4.08754e-6}, - {2.35767e-2,3.24642e-2,-6.37269e-4,-7.6916e-4,5.28004e-3,9.45642e-3}, - {-2.73743e-1,-7.40767e-1,-4.98195e-1,1.74337e-3,-1.25798e-2,-2.24046e-2}, - {-7.79128e-4,-4.14495e-4,-1.62657e-3,-1.37286e-3,1.04319e-2,1.83488e-2}, - {2.02855e-4,1.94598e-6,4.30102e-4,4.3218e-4,-3.31526e-3,-5.81788e-3} - }}, - //Li.................................................. - CoeffMat{{ - {1.e0,7.00357e-8,-3.15076e-7,-4.24915e-7,2.45516e-6,4.90187e-6}, - {3.55657e-2,4.87956e-2,-1.95525e-3,-2.7866e-3,1.6549e-2,3.11496e-2}, - {-2.81171e-1,-7.52015e-1,-4.95329e-1,5.83548e-3,-3.3983e-2,-6.55379e-2}, - {-1.83452e-3,-8.12746e-4,-3.84675e-3,-4.44467e-3,2.55871e-2,4.99483e-2}, - {4.79031e-4,-3.89615e-5,1.01022e-3,1.39133e-3,-7.99398e-3,-1.56366e-2} - }}, - //Be.................................................. - CoeffMat{{ - {1.e0,7.58881e-8,4.705e-8,2.48041e-7,-2.06053e-6,-1.97319e-6}, - {4.76788e-2,6.522e-2,-4.54331e-3,-6.50318e-3,3.76564e-2,7.17176e-2}, - {-2.88203e-1,-7.63217e-1,-4.90337e-1,1.22839e-2,-6.86398e-2,-1.35769e-1}, - {-3.37733e-3,-1.36514e-3,-7.51614e-3,-8.78592e-3,4.78572e-2,9.69021e-2}, - {8.81822e-4,-1.02577e-4,1.99797e-3,2.72661e-3,-1.48296e-2,-3.0106e-2} - }}, - //B.................................................... - CoeffMat{{ - {9.99999e-1,7.91498e-8,1.84164e-6,2.68534e-6,-1.8163e-5,-2.69021e-5}, - {5.98818e-2,8.17654e-2,-7.70811e-3,-1.12378e-2,6.38329e-2,1.25339e-1}, - {-2.94716e-1,-7.74405e-1,-4.8622e-1,1.77367e-2,-9.46825e-2,-2.01789e-1}, - {-5.52375e-3,-2.05348e-3,-9.44915e-3,-1.08135e-2,5.41024e-2,1.25257e-1}, - {1.44555e-3,-1.99404e-4,2.36742e-3,3.29655e-3,-1.64122e-2,-3.8375e-2} - }}, - //C................................................... - CoeffMat{{ - {9.99999e-1,7.68158e-8,5.18185e-6,7.34245e-6,-4.9478e-5,-7.71923e-5}, - {7.21461e-2,9.84618e-2,-1.06535e-2,-1.62358e-2,8.59238e-2,1.78727e-1}, - {-3.00622e-1,-7.85616e-1,-4.85735e-1,1.91563e-2,-8.10204e-2,-2.15386e-1}, - {-8.34809e-3,-2.85241e-3,-7.03252e-3,-7.56786e-3,1.44975e-2,8.79093e-2}, - {2.18964e-3,-3.42022e-4,1.3293e-3,2.20108e-3,-3.57927e-3,-2.5928e-2} - }}, - //N.................................................... - CoeffMat{{ - {9.99999e-1,8.36312e-8,1.09116e-5,1.47812e-5,-1.02733e-4,-1.62724e-4}, - {8.44142e-2,1.1531e-1,-1.1723e-2,-1.94732e-2,8.92604e-2,2.09303e-1}, - {-3.05743e-1,-7.96809e-1,-4.93957e-1,1.01607e-2,1.67761e-2,-1.05909e-1}, - {-1.2009e-2,-3.80678e-3,4.51195e-3,6.93472e-3,-1.12405e-1,-8.15484e-2}, - {3.16048e-3,-5.22237e-4,-2.58261e-3,-2.38303e-3,3.63393e-2,2.75127e-2} - }}, - //O................................................... - CoeffMat{{ - {9.99998e-1,1.57323e-8,1.77595e-5,2.56082e-5,-1.67537e-4,-2.73755e-4}, - {9.66438e-2,1.32264e-1,-9.53841e-3,-1.83707e-2,6.01664e-2,1.93357e-1}, - {-3.09969e-1,-8.0779e-1,-5.14392e-1,-1.67153e-2,2.3387e-1,1.916e-1}, - {-1.65906e-2,-5.11585e-3,2.80424e-2,3.94663e-2,-3.5572e-1,-4.39251e-1}, - {4.37866e-3,-6.81795e-4,-1.0152e-2,-1.24875e-2,1.11484e-1,1.38105e-1} - }}, - //F................................................. - CoeffMat{{ - {9.99997e-1,-8.06132e-8,2.49797e-5,3.8512e-5,-2.37451e-4,-3.99607e-4}, - {1.08782e-1,1.49306e-1,-2.50975e-3,-1.05471e-2,-1.64831e-2,1.05733e-1}, - {-3.13165e-1,-8.18489e-1,-5.50832e-1,-6.74447e-2,6.06357e-1,7.39717e-1}, - {-2.21976e-2,-6.84023e-3,6.65411e-2,9.48702e-2,-7.43989e-1,-1.03582e0}, - {5.87088e-3,-8.1048e-4,-2.21731e-2,-2.94422e-2,2.2954e-1,3.19669e-1} - }}, - //Ne................................................. - CoeffMat{{ - {9.99997e-1,-1.87404e-7,3.10276e-5,5.20007e-5,-2.98132e-4,-5.19259e-4}, - {1.20783e-1,1.66407e-1,1.06608e-2,6.48772e-3,-1.53031e-1,-7.59354e-2}, - {-3.15222e-1,-8.28793e-1,-6.0574e-1,-1.47812e-1,1.1576e0,1.58565e0}, - {-2.89055e-2,-9.08096e-3,1.21467e-1,1.77575e-1,-1.2911e0,-1.90333e0}, - {7.65342e-3,-8.85417e-4,-3.89092e-2,-5.4404e-2,3.93087e-1,5.79439e-1} - }}, - //Na................................................. - CoeffMat{{ - {9.99996e-1,-2.44548e-7,3.31019e-5,6.29483e-5,-3.24667e-4,-5.95527e-4}, - {1.32615e-1,1.83566e-1,3.04158e-2,3.40925e-2,-3.54681e-1,-3.63044e-1}, - {-3.16092e-1,-8.38704e-1,-6.78558e-1,-2.59346e-1,1.88547e0,2.73632e0}, - {-3.67233e-2,-1.18139e-2,1.91089e-1,2.87408e-1,-1.98397e0,-3.03075e0}, - {9.72033e-3,-9.2638e-4,-5.95654e-2,-8.69829e-2,5.95744e-1,9.10242e-1} - }}, - //Mg................................................. - CoeffMat{{ - {9.99995e-1,-2.12227e-7,2.95645e-5,6.92848e-5,-3.02153e-4,-6.05145e-4}, - {1.44258e-1,2.00775e-1,5.67845e-2,7.35166e-2,-6.22861e-1,-7.62213e-1}, - {-3.15754e-1,-8.48196e-1,-7.67318e-1,-4.02984e-1,2.77477e0,4.18114e0}, - {-4.56307e-2,-1.50425e-2,2.72232e-1,4.23528e-1,-2.79606e0,-4.38863e0}, - {1.2056e-2,-9.44637e-4,-8.28738e-2,-1.26564e-1,8.26726e-1,1.29882e0} - }}, - //Al..................................................... - CoeffMat{{ - {9.99995e-1,-4.03407e-8,1.86047e-5,6.85201e-5,-2.14503e-4,-5.22528e-4}, - {1.55704e-1,2.18048e-1,8.88994e-2,1.24878e-1,-9.51331e-1,-1.26824e0}, - {-3.14244e-1,-8.57322e-1,-8.6719e-1,-5.75787e-1,3.78571e0,5.87052e0}, - {-5.55526e-2,-1.86861e-2,3.5886e-1,5.81094e-1,-3.67623e0,-5.908e0}, - {1.46269e-2,-9.79742e-4,-1.06652e-1,-1.71226e-1,1.06737e0,1.71918e0} - }}, - //Si..................................................... - CoeffMat{{ - {9.99994e-1,3.00267e-7,-1.1184e-6,5.88256e-5,-4.78456e-5,-3.25731e-4}, - {1.6696e-1,2.35405e-1,1.25215e-1,1.87646e-1,-1.32685e0,-1.86549e0}, - {-3.1163e-1,-8.66152e-1,-9.71254e-1,-7.72715e-1,4.85654e0,7.7215e0}, - {-6.63778e-2,-2.26481e-2,4.42898e-1,7.53182e-1,-4.55172e0,-7.4867e0}, - {1.73883e-2,-1.07669e-3,-1.28075e-1,-2.18389e-1,1.29217e0,2.13475e0} - }}, - //P..................................................... - CoeffMat{{ - {9.99994e-1,8.94829e-7,-2.98434e-5,3.82193e-5,2.00584e-4,-6.40482e-6}, - {1.78039e-1,2.52912e-1,1.63761e-1,2.60132e-1,-1.73287e0,-2.53185e0}, - {-3.08007e-1,-8.74905e-1,-1.07146e0,-9.85062e-1,5.91697e0,9.63265e0}, - {-7.79747e-2,-2.66797e-2,5.15288e-1,9.29261e-1,-5.34252e0,-9.00574e0}, - {2.02892e-2,-1.33011e-3,-1.44039e-1,-2.6433e-1,1.4736e0,2.50398e0} - }}, - //S.................................................... - CoeffMat{{ - {9.99994e-1,1.75397e-6,-6.7331e-5,6.29524e-6,5.29623e-4,4.35288e-4}, - {1.88968e-1,2.70612e-1,2.01975e-1,3.40574e-1,-2.14737e0,-3.23836e0}, - {-3.03499e-1,-8.83717e-1,-1.15816e0,-1.20414e0,6.88176e0,1.14841e1}, - {-9.01806e-2,-3.06202e-2,5.65581e-1,1.09902e0,-5.95552e0,-1.03302e1}, - {2.32694e-2,-1.80614e-3,-1.51041e-1,-3.05449e-1,1.58037e0,2.78083e0} - }}, - //Cl.................................................... - CoeffMat{{ - {9.99994e-1,3.07931e-6,-1.11876e-4,-4.10164e-5,9.17095e-4,9.80145e-4}, - {1.99765e-1,2.88611e-1,2.37501e-1,4.25803e-1,-2.55105e0,-3.95585e0}, - {-2.98206e-1,-8.9293e-1,-1.22279e0,-1.4169e0,7.67836e0,1.31601e1}, - {-1.02865e-1,-3.40967e-2,5.84677e-1,1.24786e0,-6.31301e0,-1.13328e1}, - {2.628e-2,-2.63995e-3,-1.46076e-1,-3.36795e-1,1.58677e0,2.92251e0} - }}, - //Ar.................................................... - CoeffMat{{ - {9.99993e-1,4.49776e-6,-1.65136e-4,-9.76754e-5,1.39664e-3,1.66293e-3}, - {2.10469e-1,3.06924e-1,2.66793e-1,5.13797e-1,-2.90958e0,-4.63816e0}, - {-2.92294e-1,-9.0256e-1,-1.25307e0,-1.6147e0,8.18574e0,1.44912e1}, - {-1.15831e-1,-3.70891e-2,5.59807e-1,1.36619e0,-6.28824e0,-1.18327e1}, - {2.92513e-2,-3.84903e-3,-1.24976e-1,-3.55149e-1,1.45127e0,2.86925e0} - }}, - //K................................................. - CoeffMat{{ - {9.99993e-1,6.01488e-6,-2.22125e-4,-1.61774e-4,1.92058e-3,2.41975e-3}, - {2.21091e-1,3.25648e-1,2.87732e-1,6.01632e-1,-3.20436e0,-5.25724e0}, - {-2.85814e-1,-9.12889e-1,-1.24213e0,-1.78635e0,8.34196e0,1.53776e1}, - {-1.29005e-1,-3.92986e-2,4.84255e-1,1.44206e0,-5.81999e0,-1.17275e1}, - {3.21555e-2,-5.54272e-3,-8.56572e-2,-3.56589e-1,1.1548e0,2.58829e0} - }}, - //Ca................................................. - CoeffMat{{ - {9.99993e-1,8.01467e-6,-2.79242e-4,-2.3682e-4,2.45459e-3,3.21683e-3}, - {2.31651e-1,3.44948e-1,2.9782e-1,6.85187e-1,-3.41294e0,-5.77715e0}, - {-2.78858e-1,-9.24428e-1,-1.18215e0,-1.91691e0,8.07489e0,1.56969e1}, - {-1.42276e-1,-4.01888e-2,3.50466e-1,1.45983e0,-4.83806e0,-1.08936e1}, - {3.49529e-2,-7.90933e-3,-2.58002e-2,-3.36028e-1,6.7574e-1,2.04052e0} - }}, - //Sc................................................. - CoeffMat{{ - {9.99992e-1,1.04277e-5,-3.35126e-4,-3.21042e-4,2.98507e-3,4.03325e-3}, - {2.42172e-1,3.64954e-1,2.94606e-1,7.60693e-1,-3.51409e0,-6.1646e0}, - {-2.71512e-1,-9.37543e-1,-1.0657e0,-1.99328e0,7.31863e0,1.53396e1}, - {-1.5554e-1,-3.93862e-2,1.51477e-1,1.40614e0,-3.28024e0,-9.22338e0}, - {3.76066e-2,-1.10812e-2,5.66831e-2,-2.8921e-1,-4.60274e-3,1.19273e0} - }}, - //Ti................................................. - CoeffMat{{ - {9.99992e-1,1.30838e-5,-3.8407e-4,-4.09294e-4,3.47025e-3,4.81071e-3}, - {2.52646e-1,3.85718e-1,2.7633e-1,8.25665e-1,-3.48888e0,-6.39017e0}, - {-2.63758e-1,-9.52326e-1,-8.88179e-1,-2.0072e0,6.0196e0,1.42157e1}, - {-1.68806e-1,-3.68095e-2,-1.16429e-1,1.27309e0,-1.09991e0,-6.63409e0}, - {4.01184e-2,-1.50938e-2,1.6274e-1,-2.1376e-1,-8.99142e-1,2.08129e-2} - }}, - //V................................................. - CoeffMat{{ - {9.99991e-1,1.59363e-5,-4.27315e-4,-5.01461e-4,3.91365e-3,5.55096e-3}, - {2.63096e-1,4.07357e-1,2.40649e-1,8.7645e-1,-3.31641e0,-6.42069e0}, - {-2.55682e-1,-9.6909e-1,-6.43149e-1,-1.94678e0,4.11915e0,1.22255e1}, - {-1.81974e-1,-3.21462e-2,-4.58817e-1,1.04913e0,1.75388e0,-3.03434e0}, - {4.24541e-2,-2.00595e-2,2.93916e-1,-1.06138e-1,-2.02203e0,-1.50181e0} - }}, - //Cr................................................. - CoeffMat{{ - {9.9999e-1,1.8895e-5,-4.59994e-4,-5.93663e-4,4.27684e-3,6.2009e-3}, - {2.73504e-1,4.2999e-1,1.86473e-1,9.09921e-1,-2.98441e0,-6.23344e0}, - {-2.47225e-1,-9.88118e-1,-3.28759e-1,-1.80252e0,1.5909e0,9.30968e0}, - {-1.951e-1,-2.51242e-2,-8.76244e-1,7.25592e-1,5.2965e0,1.62177e0}, - {4.46307e-2,-2.60754e-2,4.50054e-1,3.61638e-2,-3.37524e0,-3.38601e0} - }}, - //Mn................................................. - CoeffMat{{ - {9.99989e-1,2.18906e-5,-4.79199e-4,-6.83164e-4,4.53467e-3,6.72485e-3}, - {2.83854e-1,4.53722e-1,1.12877e-1,9.23203e-1,-2.48247e0,-5.80855e0}, - {-2.38338e-1,-1.00965e0,5.61453e-2,-1.56605e0,-1.58353e0,5.42138e0}, - {-2.08226e-1,-1.55138e-2,-1.36846e0,2.95168e-1,9.53392e0,7.3655e0}, - {4.66619e-2,-3.32255e-2,6.30711e-1,2.15167e-1,-4.95748e0,-5.63744e0} - }}, - //Fe................................................... - CoeffMat{{ - {9.99987e-1,2.482e-5,-4.82895e-4,-7.67488e-4,4.669e-3,7.09581e-3}, - {2.94123e-1,4.78653e-1,1.93256e-2,9.13569e-1,-1.80354e0,-5.1304e0}, - {-2.28945e-1,-1.0339e0,5.1121e-1,-1.22996e0,-5.40939e0,5.3149e-1}, - {-2.21426e-1,-3.12679e-3,-1.93353e0,-2.48179e-1,1.44576e1,1.42077e1}, - {4.85718e-2,-4.15796e-2,8.34879e-1,4.32418e-1,-6.76244e0,-8.25464e0} - }}, - //Co.................................................. - CoeffMat{{ - {9.99985e-1,2.76168e-5,-4.67522e-4,-8.43978e-4,4.65008e-3,7.27476e-3}, - {3.0428e-1,5.04856e-1,-9.42256e-2,8.78905e-1,-9.44256e-1,-4.18847e0}, - {-2.18939e-1,-1.06098e0,1.03431e0,-7.89123e-1,-9.87815e0,-5.36997e0}, - {-2.34805e-1,1.21281e-2,-2.56765e0,-9.08015e-1,2.00436e1,2.21379e1}, - {5.03942e-2,-5.11768e-2,1.0609e0,6.88631e-1,-8.77867e0,-1.12289e1} - }}, - //Ni.................................................. - CoeffMat{{ - {9.99982e-1,3.00792e-5,-4.33447e-4,-9.09366e-4,4.47786e-3,7.25072e-3}, - {3.14283e-1,5.32444e-1,-2.27528e-1,8.16951e-1,9.53704e-2,-2.9773e0}, - {-2.08186e-1,-1.09112e0,1.62237e0,-2.38394e-1,-1.49699e1,-1.22743e1}, - {-2.48493e-1,3.04543e-2,-3.26601e0,-1.6876e0,2.62568e1,3.11259e1}, - {5.21709e-2,-6.20908e-2,1.30683e0,9.84353e-1,-1.09909e1,-1.45449e1} - }}, - //Cu................................................... - CoeffMat{{ - {9.99979e-1,3.24569e-5,-3.76717e-4,-9.64e-4,4.11997e-3,6.98761e-3}, - {3.24082e-1,5.61534e-1,-3.79993e-1,7.25313e-1,1.31342e0,-1.49163e0}, - {-1.96521e-1,-1.12457e0,2.27091e0,4.27678e-1,-2.06558e1,-2.0169e1}, - {-2.62655e-1,5.20812e-2,-4.02224e0,-2.59048e0,3.30508e1,4.1135e1}, - {5.39556e-2,-7.44077e-2,1.57015e0,1.32022e0,-1.33802e1,-1.81848e1} - }}, - //Zn................................................... - CoeffMat{{ - {9.99976e-1,3.39628e-5,-2.98845e-4,-9.99651e-4,3.58523e-3,6.47782e-3}, - {3.33647e-1,5.92068e-1,-5.5102e-1,6.0363e-1,2.70712e0,2.67853e-1}, - {-1.83849e-1,-1.16095e0,2.97559e0,1.20728e0,-2.69053e1,-2.90214e1}, - {-2.77375e-1,7.65838e-2,-4.83023e0,-3.61238e0,4.03786e1,5.21084e1}, - {5.57745e-2,-8.79952e-2,1.84848e0,1.69425e0,-1.59274e1,-2.21242e1} - }}, - //Ga................................................... - CoeffMat{{ - {9.99972e-1,3.48473e-5,-1.97828e-4,-1.01659e-3,2.85509e-3,5.69725e-3}, - {3.42904e-1,6.24164e-1,-7.39023e-1,4.50306e-1,4.26553e0,2.29299e0}, - {-1.6994e-1,-1.20051e0,3.72868e0,2.10276e0,-3.36594e1,-3.87726e1}, - {-2.92882e-1,1.04194e-1,-5.68033e0,-4.75339e0,4.81633e1,6.39614e1}, - {5.77008e-2,-1.02941e-1,2.13826e0,2.10589e0,-1.86034e1,-2.63294e1} - }}, - //Ge................................................. - CoeffMat{{ - {9.99968e-1,3.47804e-5,-7.11898e-5,-1.01028e-3,1.90919e-3,4.61426e-3}, - {3.51793e-1,6.57815e-1,-9.42175e-1,2.6494e-1,5.97606e0,4.57222e0}, - {-1.54595e-1,-1.24305e0,4.5217e0,3.11205e0,-4.08539e1,-4.93518e1}, - {-3.09367e-1,1.34661e-1,-6.56214e0,-6.00882e0,5.63228e1,7.65965e1}, - {5.97948e-2,-1.19174e-1,2.4357e0,2.553e0,-2.13779e1,-3.07628e1} - }}, - //As................................................. - CoeffMat{{ - {9.99963e-1,3.37519e-5,8.13037e-5,-9.78638e-4,7.41412e-4,3.21498e-3}, - {3.60246e-1,6.93065e-1,-1.1588e0,4.68519e-2,7.82662e0,7.09456e0}, - {-1.37615e-1,-1.28855e0,5.34673e0,4.23402e0,-4.84263e1,-6.06897e1}, - {-3.27017e-1,1.67941e-1,-7.46593e0,-7.37492e0,6.47773e1,8.99181e1}, - {6.21158e-2,-1.36692e-1,2.73726e0,3.03374e0,-2.42209e1,-3.53873e1} - }}, - //Se................................................. - CoeffMat{{ - {9.99958e-1,3.14983e-5,2.59741e-4,-9.19008e-4,-6.49202e-4,1.49008e-3}, - {3.68196e-1,7.29888e-1,-1.38664e0,-2.03807e-1,9.80062e0,9.84155e0}, - {-1.18788e-1,-1.33674e0,6.194e0,5.46446e0,-5.62994e1,-7.26915e1}, - {-3.46033e-1,2.03729e-1,-8.38012e0,-8.84466e0,7.34319e1,1.03805e2}, - {6.47255e-2,-1.55409e-1,3.03879e0,3.54516e0,-2.7098e1,-4.01572e1} - }}, - //Br................................................. - CoeffMat{{ - {9.99952e-1,2.79961e-5,4.60479e-4,-8.33486e-4,-2.23214e-3,-5.16285e-4}, - {3.7558e-1,7.68292e-1,-1.62392e0,-4.87674e-1,1.18854e1,1.28025e1}, - {-9.79355e-2,-1.38749e0,7.0556e0,6.80198e0,-6.44113e1,-8.52915e1}, - {-3.66572e-1,2.41863e-1,-9.29524e0,-1.04141e1,8.22093e1,1.18166e2}, - {6.76714e-2,-1.75289e-1,3.33691e0,4.08538e0,-2.99809e1,-4.50381e1} - }}, - //Kr................................................. - CoeffMat{{ - {9.99947e-1,2.2952e-5,6.82639e-4,-7.17139e-4,-4.00522e-3,-2.81601e-3}, - {3.82332e-1,8.08194e-1,-1.86848e0,-8.03415e-1,1.4064e1,1.5956e1}, - {-7.48735e-2,-1.44031e0,7.92233e0,8.2382e0,-7.26858e1,-9.83865e1}, - {-3.88797e-1,2.81837e-1,-1.02005e1,-1.20716e1,9.10175e1,1.32873e2}, - {7.10017e-2,-1.96182e-1,3.6278e0,4.65e0,-3.28365e1,-4.99823e1} - }}, - //Rb................................................. - CoeffMat{{ - {9.99941e-1,1.63607e-5,9.28242e-4,-5.69257e-4,-5.98245e-3,-5.42476e-3}, - {3.88363e-1,8.49548e-1,-2.11701e0,-1.15003e0,1.63119e1,1.92737e1}, - {-4.93364e-2,-1.49491e0,8.78129e0,9.76596e0,-8.10212e1,-1.11851e2}, - {-4.12953e-1,3.23322e-1,-1.10814e1,-1.38073e1,9.97386e1,1.47775e2}, - {7.47911e-2,-2.18001e-1,3.90642e0,5.23509e0,-3.56231e1,-5.49352e1} - }}, - //Sr................................................. - CoeffMat{{ - {9.99935e-1,8.3152e-6,1.19244e-3,-3.95258e-4,-8.12525e-3,-8.28836e-3}, - {3.93603e-1,8.92358e-1,-2.36688e0,-1.52786e0,1.86071e1,2.27314e1}, - {-2.11345e-2,-1.55112e0,9.62205e0,1.13827e1,-8.93266e1,-1.25574e2}, - {-4.39199e-1,3.66161e-1,-1.19262e1,-1.56159e1,1.08267e2,1.62736e2}, - {7.90853e-2,-2.40719e-1,4.16872e0,5.83839e0,-3.8304e1,-5.98484e1} - }}, - //Y................................................. - CoeffMat{{ - {9.99929e-1,-1.65608e-6,1.47476e-3,-1.8625e-4,-1.04363e-2,-1.14318e-2}, - {3.97989e-1,9.3643e-1,-2.61569e0,-1.93387e0,2.09305e1,2.63021e1}, - {9.88744e-3,-1.60813e0,1.04351e1,1.3074e1,-9.75207e1,-1.39437e2}, - {-4.67657e-1,4.09494e-1,-1.2724e1,-1.74797e1,1.16508e2,1.77614e2}, - {8.39169e-2,-2.64077e-1,4.41092e0,6.45344e0,-4.08455e1,-6.46698e1} - }}, - //Zr................................................. - CoeffMat{{ - {9.99922e-1,-1.37624e-5,1.77335e-3,5.994e-5,-1.29013e-2,-1.48443e-2}, - {4.0145e-1,9.816e-1,-2.86053e0,-2.36562e0,2.32591e1,2.99551e1}, - {4.3915e-2,-1.66522e0,1.12093e1,1.48279e1,-1.05512e2,-1.5331e2}, - {-4.98475e-1,4.52607e-1,-1.34628e1,-1.93836e1,1.24357e2,1.92256e2}, - {8.93258e-2,-2.87869e-1,4.62891e0,7.07473e0,-4.32116e1,-6.93461e1} - }}, - //Nb................................................. - CoeffMat{{ - {9.99916e-1,-2.78975e-5,2.08753e-3,3.41607e-4,-1.55141e-2,-1.85156e-2}, - {4.03889e-1,1.02783e0,-3.09762e0,-2.82183e0,2.55629e1,3.36548e1}, - {8.12122e-2,-1.7221e0,1.1931e1,1.66362e1,-1.13185e2,-1.67048e2}, - {-5.3188e-1,4.95236e-1,-1.41275e1,-2.13166e1,1.31687e2,2.06495e2}, - {9.53781e-2,-3.12041e-1,4.81765e0,7.69813e0,-4.53587e1,-7.38182e1} - }}, - //Mo................................................. - CoeffMat{{ - {9.9991e-1,-4.3988e-5,2.41439e-3,6.54636e-4,-1.82489e-2,-2.24062e-2}, - {4.05249e-1,1.07495e0,-3.32445e0,-3.30073e0,2.78221e1,3.7376e1}, - {1.21896e-1,-1.77813e0,1.25907e1,1.84893e1,-1.20461e2,-1.80542e2}, - {-5.67942e-1,5.36741e-1,-1.4708e1,-2.32664e1,1.38408e2,2.20203e2}, - {1.02086e-1,-3.36419e-1,4.97373e0,8.31905e0,-4.72562e1,-7.80409e1} - }}, - //Tc................................................. - CoeffMat{{ - {9.99904e-1,-6.22073e-5,2.75066e-3,1.00063e-3,-2.10817e-2,-2.64933e-2}, - {4.05472e-1,1.12279e0,-3.53825e0,-3.79964e0,3.00132e1,4.10871e1}, - {1.66089e-1,-1.83255e0,1.31785e1,2.03743e1,-1.27252e2,-1.93664e2}, - {-6.06729e-1,5.76392e-1,-1.51936e1,-2.52171e1,1.44423e2,2.33232e2}, - {1.09463e-1,-3.60803e-1,5.09358e0,8.93178e0,-4.88706e1,-8.19632e1} - }}, - //Ru................................................. - CoeffMat{{ - {9.99898e-1,-8.26232e-5,3.09353e-3,1.37924e-3,-2.39902e-2,-3.07502e-2}, - {4.04498e-1,1.17115e0,-3.73628e0,-4.3158e0,3.21142e1,4.4758e1}, - {2.13904e-1,-1.88458e0,1.36848e1,2.22782e1,-1.33471e2,-2.06293e2}, - {-6.48301e-1,6.13439e-1,-1.55742e1,-2.71531e1,1.49638e2,2.45444e2}, - {1.17516e-1,-3.8499e-1,5.17383e0,9.5307e0,-5.01709e1,-8.55368e1} - }}, - //Rh................................................. - CoeffMat{{ - {9.99893e-1,-1.05293e-4,3.43932e-3,1.78981e-3,-2.69462e-2,-3.51436e-2}, - {4.02272e-1,1.21982e0,-3.91575e0,-4.84621e0,3.41013e1,4.83563e1}, - {2.65438e-1,-1.93341e0,1.40998e1,2.41876e1,-1.39034e2,-2.183e2}, - {-6.92691e-1,6.47135e-1,-1.58398e1,-2.90581e1,1.5396e2,2.56697e2}, - {1.26243e-1,-4.08783e-1,5.21125e0,1.011e1,-5.11262e1,-8.8713e1} - }}, - //Pd................................................. - CoeffMat{{ - {9.99888e-1,-1.30188e-4,3.78436e-3,2.22971e-3,-2.99185e-2,-3.96315e-2}, - {3.9875e-1,1.26855e0,-4.07427e0,-5.38796e0,3.5955e1,5.18546e1}, - {3.20748e-1,-1.97817e0,1.44153e1,2.6089e1,-1.43866e2,-2.29578e2}, - {-7.39891e-1,6.76679e-1,-1.59819e1,-3.09162e1,1.57312e2,2.66867e2}, - {1.3563e-1,-4.31972e-1,5.2031e0,1.06642e1,-5.17104e1,-9.14493e1} - }}, - //Ag................................................. - CoeffMat{{ - {9.99884e-1,-1.57459e-4,4.12052e-3,2.69984e-3,-3.28468e-2,-4.41512e-2}, - {3.93891e-1,1.3171e0,-4.20944e0,-5.93691e0,3.76535e1,5.52188e1}, - {3.79857e-1,-2.01791e0,1.46238e1,2.79653e1,-1.47893e2,-2.39997e2}, - {-7.89852e-1,7.01196e-1,-1.59929e1,-3.27077e1,1.59613e2,2.75813e2}, - {1.45646e-1,-4.54328e-1,5.14701e0,1.11863e1,-5.18977e1,-9.36987e1} - }}, - //Cd................................................. - CoeffMat{{ - {9.99881e-1,-1.86816e-4,4.45491e-3,3.19574e-3,-3.57812e-2,-4.87457e-2}, - {3.87612e-1,1.36524e0,-4.31686e0,-6.49005e0,3.91612e1,5.84032e1}, - {4.4294e-1,-2.05189e0,1.47103e1,2.98033e1,-1.50988e2,-2.49389e2}, - {-8.42683e-1,7.20045e-1,-1.58579e1,-3.44169e1,1.60733e2,2.83354e2}, - {1.56312e-1,-4.75705e-1,5.0381e0,1.16708e1,-5.16454e1,-9.53999e1} - }}, - //In................................................. - CoeffMat{{ - {9.99878e-1,-2.1848e-4,4.78376e-3,3.71897e-3,-3.86926e-2,-5.33866e-2}, - {3.79879e-1,1.41265e0,-4.39391e0,-7.04282e0,4.0455e1,6.13721e1}, - {5.10001e-1,-2.07896e0,1.46666e1,3.15843e1,-1.53072e2,-2.57624e2}, - {-8.98306e-1,7.32213e-1,-1.55686e1,-3.60229e1,1.60591e2,2.89348e2}, - {1.67591e-1,-4.95837e-1,4.87389e0,1.21106e1,-5.09274e1,-9.6506e1} - }}, - //Sn................................................ - CoeffMat{{ - {9.99876e-1,-2.52173e-4,5.09558e-3,4.26518e-3,-4.14939e-2,-5.79712e-2}, - {3.70689e-1,1.45908e0,-4.43983e0,-7.59192e0,4.15261e1,6.4108e1}, - {5.80924e-1,-2.09831e0,1.44911e1,3.32945e1,-1.54118e2,-2.64634e2}, - {-9.56518e-1,7.37009e-1,-1.51243e1,-3.75098e1,1.59159e2,2.93723e2}, - {1.79397e-1,-5.14573e-1,4.65426e0,1.25003e1,-4.97362e1,-9.69932e1} - }}, - //Sb................................................. - CoeffMat{{ - {9.99874e-1,-2.87917e-4,5.39066e-3,4.83313e-3,-4.41852e-2,-6.24953e-2}, - {3.60001e-1,1.5042e0,-4.4515e0,-8.1327e0,4.23476e1,6.65709e1}, - {6.55716e-1,-2.10882e0,1.41741e1,3.49157e1,-1.54034e2,-2.70276e2}, - {-1.01724e0,7.33501e-1,-1.45156e1,-3.88572e1,1.56348e2,2.96328e2}, - {1.9169e-1,-5.3169e-1,4.37639e0,1.28328e1,-4.80435e1,-9.68125e1} - }}, - //Te................................................. - CoeffMat{{ - {9.99874e-1,-3.25847e-4,5.67083e-3,5.42945e-3,-4.67877e-2,-6.701e-2}, - {3.47783e-1,1.54767e0,-4.42606e0,-8.65995e0,4.28934e1,6.87183e1}, - {7.34353e-1,-2.10941e0,1.3707e1,3.64273e1,-1.52736e2,-2.74401e2}, - {-1.08036e0,7.20771e-1,-1.37344e1,-4.00427e1,1.52073e2,2.97007e2}, - {2.04417e-1,-5.46972e-1,4.03784e0,1.31007e1,-4.58224e1,-9.59123e1} - }}, - //I................................................. - CoeffMat{{ - {9.99875e-1,-3.65406e-4,5.92115e-3,6.03332e-3,-4.91674e-2,-7.12951e-2}, - {3.34062e-1,1.58924e0,-4.36361e0,-9.17241e0,4.31638e1,7.05522e1}, - {8.16604e-1,-2.09933e0,1.30915e1,3.78235e1,-1.50231e2,-2.77015e2}, - {-1.14554e0,6.98298e-1,-1.2784e1,-4.10591e1,1.46348e2,2.9577e2}, - {2.17451e-1,-5.60344e-1,3.63994e0,1.33015e1,-4.30802e1,-9.42981e1} - }}, - //Xe.................................................. - CoeffMat{{ - {9.99877e-1,-4.06637e-4,6.14278e-3,6.64937e-3,-5.13391e-2,-7.53881e-2}, - {3.18822e-1,1.62854e0,-4.26169e0,-9.66459e0,4.31357e1,7.20331e1}, - {9.02373e-1,-2.07743e0,1.23209e1,3.90832e1,-1.46445e2,-2.77984e2}, - {-1.21259e0,6.65174e-1,-1.16583e1,-4.18839e1,1.39104e2,2.92479e2}, - {2.3071e-1,-5.71608e-1,3.18104e0,1.34277e1,-3.97963e1,-9.19255e1} - }}, - //Cs.................................................. - CoeffMat{{ - {9.99881e-1,-4.49175e-4,6.32631e-3,7.27184e-3,-5.32297e-2,-7.91972e-2}, - {3.02086e-1,1.66529e0,-4.11996e0,-1.0133e1,4.28031e1,7.31464e1}, - {9.91429e-1,-2.04292e0,1.13959e1,4.01931e1,-1.41369e2,-2.7726e2}, - {-1.28117e0,6.2087e-1,-1.03596e1,-4.25024e1,1.30338e2,2.8709e2}, - {2.44065e-1,-5.80702e-1,2.66217e0,1.34745e1,-3.59721e1,-8.87824e1} - }}, - //Ba.................................................. - CoeffMat{{ - {9.99886e-1,-4.93136e-4,6.47914e-3,7.90029e-3,-5.48909e-2,-8.27736e-2}, - {2.8384e-1,1.69905e0,-3.93534e0,-1.05724e1,4.21392e1,7.38521e1}, - {1.08366e0,-1.99451e0,1.03076e1,4.1134e1,-1.34917e2,-2.7471e2}, - {-1.35107e0,5.64397e-1,-8.87993e0,-4.28945e1,1.19972e2,2.7947e2}, - {2.57431e-1,-5.87417e-1,2.08111e0,1.34353e1,-3.15844e1,-8.48268e1} - }}, - //La.................................................. - CoeffMat{{ - {9.99892e-1,-5.38122e-4,6.59206e-3,8.52848e-3,-5.62508e-2,-8.60251e-2}, - {2.6413e-1,1.72952e0,-3.70812e0,-1.09794e1,4.1143e1,7.4141e1}, - {1.17876e0,-1.93141e0,9.05928e0,4.18928e1,-1.27099e2,-2.70309e2}, - {-1.42185e0,4.95292e-1,-7.22427e0,-4.30461e1,1.08024e2,2.69601e2}, - {2.70647e-1,-5.91727e-1,1.43982e0,1.33056e1,-2.66423e1,-8.00557e1} - }}, - //Ce.................................................. - CoeffMat{{ - {9.999e-1,-5.83896e-4,6.66373e-3,9.15389e-3,-5.72988e-2,-8.89368e-2}, - {2.42961e-1,1.75635e0,-3.43619e0,-1.13482e1,3.97928e1,7.39747e1}, - {1.27654e0,-1.85263e0,7.64589e0,4.24492e1,-1.17853e2,-2.63933e2}, - {-1.49322e0,4.12926e-1,-5.38909e0,-4.29363e1,9.44419e1,2.57363e2}, - {2.83597e-1,-5.93557e-1,7.3754e-1,1.30785e1,-2.11316e1,-7.44329e1} - }}, - //Pr.................................................. - CoeffMat{{ - {9.99909e-1,-6.301e-4,6.69295e-3,9.77077e-3,-5.8022e-2,-9.14799e-2}, - {2.20373e-1,1.77919e0,-3.11907e0,-1.16754e1,3.80818e1,7.33384e1}, - {1.37668e0,-1.75734e0,6.06819e0,4.27905e1,-1.07169e2,-2.55543e2}, - {-1.56475e0,3.16885e-1,-3.37687e0,-4.25518e1,7.92269e1,2.42726e2}, - {2.96124e-1,-5.92915e-1,-2.45072e-2,1.27501e1,-1.50562e1,-6.79514e1} - }}, - //Nd.................................................. - CoeffMat{{ - {9.9992e-1,-6.77211e-4,6.67917e-3,1.03866e-2,-5.84208e-2,-9.36775e-2}, - {1.96406e-1,1.79753e0,-2.7553e0,-1.19533e1,3.59929e1,7.21943e1}, - {1.47887e0,-1.64416e0,4.32381e0,4.28906e1,-9.50034e1,-2.45021e2}, - {-1.636e0,2.06169e-1,-1.18704e0,-4.18661e1,6.23476e1,2.25579e2}, - {3.08067e-1,-5.89622e-1,-8.4608e-1,1.23116e1,-8.40939e0,-6.05788e1} - }}, - //Pm.................................................. - CoeffMat{{ - {9.99932e-1,-7.23543e-4,6.61026e-3,1.09756e-2,-5.83892e-2,-9.53462e-2}, - {1.71135e-1,1.81127e0,-2.34665e0,-1.21818e1,3.35368e1,7.05547e1}, - {1.58269e0,-1.5131e0,2.4214e0,4.27487e1,-8.14091e1,-2.32422e2}, - {-1.70642e0,8.13302e-2,1.16945e0,-4.0879e1,4.38728e1,2.05995e2}, - {3.19226e-1,-5.84023e-1,-1.72313e0,1.17636e1,-1.21776e0,-5.23434e1} - }}, - //Sm.................................................. - CoeffMat{{ - {9.99946e-1,-7.69368e-4,6.48923e-3,1.15425e-2,-5.79523e-2,-9.65284e-2}, - {1.44613e-1,1.81998e0,-1.89196e0,-1.23548e1,3.06998e1,6.83894e1}, - {1.68776e0,-1.36316e0,3.59801e-1,4.23443e1,-6.63559e1,-2.17658e2}, - {-1.77552e0,-5.81387e-2,3.69184e0,-3.95701e1,2.37855e1,1.83897e2}, - {3.2942e-1,-5.76119e-1,-2.65496e0,1.10995e1,6.52027e0,-4.32241e1} - }}, - //Eu.................................................. - CoeffMat{{ - {9.99962e-1,-8.13654e-4,6.31217e-3,1.20763e-2,-5.70782e-2,-9.71683e-2}, - {1.16915e-1,1.82343e0,-1.39208e0,-1.24689e1,2.74833e1,6.56914e1}, - {1.79364e0,-1.194e0,-1.85513e0,4.16665e1,-4.9868e1,-2.00724e2}, - {-1.84272e0,-2.12012e-1,6.37189e0,-3.79293e1,2.12742e0,1.59295e2}, - {3.38449e-1,-5.66167e-1,-3.63838e0,1.03165e1,1.47866e1,-3.32296e1} - }}, - //Gd.................................................. - CoeffMat{{ - {9.9998e-1,-8.57331e-4,6.07736e-3,1.25819e-2,-5.57471e-2,-9.72475e-2}, - {8.8154e-2,1.82105e0,-8.47718e-1,-1.25186e1,2.38891e1,6.2451e1}, - {1.89976e0,-1.00428e0,-4.21799e0,4.0697e1,-3.19703e1,-1.81604e2}, - {-1.90731e0,-3.8108e-1,9.2019e0,-3.59386e1,-2.10594e1,1.32192e2}, - {3.46059e-1,-5.54093e-1,-4.67039e0,9.40924e0,2.35631e1,-2.23653e1} - }}, - //Tb.................................................. - CoeffMat{{ - {9.99999e-1,-8.99343e-4,5.7868e-3,1.30512e-2,-5.39764e-2,-9.67723e-2}, - {5.84024e-2,1.81266e0,-2.59193e-1,-1.25002e1,1.99139e1,5.86545e1}, - {2.00567e0,-7.93885e-1,-6.72461e0,3.94244e1,-1.26714e1,-1.60273e2}, - {-1.96873e0,-5.64782e-1,1.21753e1,-3.3588e1,-4.57487e1,1.02578e2}, - {3.5205e-1,-5.40282e-1,-5.74837e0,8.37487e0,3.28364e1,-1.06341e1} - }}, - //Dy.................................................. - CoeffMat{{ - {1.00002e0,-9.39311e-4,5.43607e-3,1.34763e-2,-5.17249e-2,-9.56765e-2}, - {2.78005e-2,1.79795e0,3.70737e-1,-1.24114e1,1.55759e1,5.43184e1}, - {2.1107e0,-5.62473e-1,-9.36239e0,3.78424e1,7.94516e0,-1.36806e2}, - {-2.02617e0,-7.62769e-1,1.52768e1,-3.08728e1,-7.18354e1,7.05554e1}, - {3.5614e-1,-5.25061e-1,-6.86679e0,7.21247e0,4.25672e1,1.92463e0} - }}, - //Ho................................................... - CoeffMat{{ - {1.00004e0,-9.76747e-4,5.02266e-3,1.3853e-2,-4.89737e-2,-9.3934e-2}, - {-3.54744e-3,1.77664e0,1.04132e0,-1.22473e1,1.08741e1,4.94269e1}, - {2.21429e0,-3.09743e-1,-1.21254e1,3.59359e1,2.986e1,-1.11177e2}, - {-2.07893e0,-9.74603e-1,1.84979e1,-2.77795e1,-9.928e1,3.61173e1}, - {3.5809e-1,-5.08796e-1,-8.02233e0,5.91833e0,5.27374e1,1.53069e1} - }}, - //Er................................................... - CoeffMat{{ - {1.00007e0,-1.01117e-3,4.54349e-3,1.4172e-2,-4.56907e-2,-9.14834e-2}, - {-3.54805e-2,1.74848e0,1.74921e0,-1.20063e1,5.83106e0,4.40029e1}, - {2.3157e0,-3.56472e-2,-1.49988e1,3.37013e1,5.29727e1,-8.34879e1}, - {-2.12613e0,-1.19953e0,2.18208e1,-2.43067e1,-1.27958e2,-6.05797e-1}, - {3.57593e-1,-4.91966e-1,-9.2087e0,4.49274e0,6.33009e1,2.94631e1} - }}, - //Tm................................................... - CoeffMat{{ - {1.00009e0,-1.04219e-3,3.99979e-3,1.44301e-2,-4.18849e-2,-8.83308e-2}, - {-6.78832e-2,1.71323e0,2.49363e0,-1.16834e1,4.45978e-1,3.80313e1}, - {2.41433e0,2.59886e-1,-1.79765e1,3.11249e1,7.72617e1,-5.37152e1}, - {-2.16704e0,-1.4368e0,2.52369e1,-2.04429e1,-1.57828e2,-3.9614e1}, - {3.544e-1,-4.75067e-1,-1.04225e1,2.93269e0,7.42385e1,4.43866e1} - }}, - //Yb................................................... - CoeffMat{{ - {1.00012e0,-1.06927e-3,3.39091e-3,1.46208e-2,-3.75471e-2,-8.44506e-2}, - {-1.00611e-1,1.67068e0,3.27246e0,-1.12756e1,-5.27058e0,3.15152e1}, - {2.50948e0,5.76648e-1,-2.10477e1,2.81988e1,1.02665e2,-2.18989e1}, - {-2.20084e0,-1.68528e0,2.87325e1,-1.61828e1,-1.88802e2,-8.08394e1}, - {3.48226e-1,-4.58722e-1,-1.16589e1,1.23735e0,8.55166e1,6.00477e1} - }}, - //Lu................................................... - CoeffMat{{ - {1.00015e0,-1.09216e-3,2.71745e-3,1.47404e-2,-3.26779e-2,-7.98343e-2}, - {-1.33495e-1,1.62059e0,4.08307e0,-1.07793e1,-1.13042e1,2.44607e1}, - {2.60038e0,9.14519e-1,-2.42e1,2.49142e1,1.29108e2,1.19101e1}, - {-2.22661e0,-1.94391e0,3.22924e1,-1.15206e1,-2.20784e2,-1.24202e2}, - {3.38764e-1,-4.43546e-1,-1.29122e1,-5.9428e-1,9.70972e1,7.64128e1} - }}, - //Hf.................................................. - CoeffMat{{ - {1.00018e0,-1.11005e-3,1.98019e-3,1.47814e-2,-2.72814e-2,-7.44728e-2}, - {-1.66392e-1,1.56288e0,4.92359e0,-1.01915e1,-1.76472e1,1.68656e1}, - {2.68631e0,1.27279e0,-2.74231e1,2.12642e1,1.56537e2,4.76851e1}, - {-2.24352e0,-2.21096e0,3.59035e1,-6.45262e0,-2.53694e2,-1.69646e2}, - {3.25734e-1,-4.30391e-1,-1.41779e1,-2.56236e0,1.08949e2,9.34555e1} - }}, - //Ta.................................................. - CoeffMat{{ - {1.00021e0,-1.1227e-3,1.1875e-3,1.47467e-2,-2.14258e-2,-6.84575e-2}, - {-1.99145e-1,1.49739e0,5.79226e0,-9.50812e0,-2.42938e1,8.72312e0}, - {2.76656e0,1.65088e0,-3.07077e1,1.72391e1,1.84905e2,8.54178e1}, - {-2.25073e0,-2.48478e0,3.95541e1,-9.72452e-1,-2.87462e2,-2.17133e2}, - {3.0885e-1,-4.20088e-1,-1.54515e1,-4.66797e0,1.21043e2,1.11156e2} - }}, - //W................................................... - CoeffMat{{ - {1.00025e0,-1.12974e-3,3.25083e-4,1.46196e-2,-1.49818e-2,-6.15916e-2}, - {-2.31499e-1,1.42403e0,6.68301e0,-8.72894e0,-3.12005e1,8.13039e-2}, - {2.84004e0,2.04799e0,-3.40292e1,1.28426e1,2.14035e2,1.24907e2}, - {-2.24701e0,-2.76343e0,4.3216e1,4.91177e0,-3.2188e2,-2.66423e2}, - {2.87708e-1,-4.13579e-1,-1.67235e1,-6.90724e0,1.33305e2,1.29426e2} - }}, - //Re.................................................. - CoeffMat{{ - {1.00028e0,-1.13018e-3,-5.85208e-4,1.44008e-2,-8.12721e-3,-5.40793e-2}, - {-2.63328e-1,1.34285e0,7.59541e0,-7.85118e0,-3.83723e1,-9.07534e0}, - {2.90612e0,2.46267e0,-3.73829e1,8.06978e0,2.43918e2,1.66172e2}, - {-2.23164e0,-3.04426e0,4.68824e1,1.12007e1,-3.56918e2,-3.17506e2}, - {2.62068e-1,-4.12056e-1,-1.79911e1,-9.27922e0,1.45719e2,1.48255e2} - }}, - //Os.................................................. - CoeffMat{{ - {1.00032e0,-1.12442e-3,-1.55891e-3,1.40881e-2,-7.3226e-4,-4.57754e-2}, - {-2.94323e-1,1.25374e0,8.52167e0,-6.87354e0,-4.57537e1,-1.8691e1}, - {2.96357e0,2.89403e0,-4.07385e1,2.92128e0,2.74335e2,2.08986e2}, - {-2.20324e0,-3.32515e0,5.05197e1,1.78893e1,-3.92323e2,-3.70113e2}, - {2.31474e-1,-4.16536e-1,-1.92429e1,-1.1781e1,1.58197e2,1.67545e2} - }}, - //Ir................................................... - CoeffMat{{ - {1.00036e0,-1.11107e-3,-2.58076e-3,1.36692e-2,7.08911e-3,-3.6773e-2}, - {-3.24316e-1,1.15695e0,9.45947e0,-5.79675e0,-5.33328e1,-2.87528e1}, - {3.01159e0,3.33981e0,-4.4085e1,-2.59468e0,3.05217e2,2.53266e2}, - {-2.16093e0,-3.60244e0,5.41141e1,2.49638e1,-4.28002e2,-4.24126e2}, - {1.95645e-1,-4.28567e-1,-2.04742e1,-1.44066e1,1.70702e2,1.87249e2} - }}, - //Pt................................................... - CoeffMat{{ - {1.00039e0,-1.09011e-3,-3.65347e-3,1.31413e-2,1.53649e-2,-2.70335e-2}, - {-3.53028e-1,1.05257e0,1.04028e1,-4.62034e0,-6.10681e1,-3.92186e1}, - {3.04907e0,3.79831e0,-4.73979e1,-8.47391e0,3.36394e2,2.98829e2}, - {-2.10347e0,-3.87306e0,5.76385e1,3.24147e1,-4.63751e2,-4.79318e2}, - {1.54174e-1,-4.49506e-1,-2.16756e1,-1.71516e1,1.83161e2,2.07283e2} - }}, - //Au................................................... - CoeffMat{{ - {1.00043e0,-1.06114e-3,-4.7734e-3,1.25001e-2,2.40713e-2,-1.65728e-2}, - {-3.80207e-1,9.40778e-1,1.13464e1,-3.34423e0,-6.89259e1,-5.00549e1}, - {3.07493e0,4.26732e0,-5.06567e1,-1.47102e1,3.67721e2,3.45521e2}, - {-2.0297e0,-4.13334e0,6.10696e1,4.02298e1,-4.99399e2,-5.35495e2}, - {1.06694e-1,-4.80932e-1,-2.28393e1,-2.00106e1,1.95512e2,2.27574e2} - }}, - //Hg................................................. - CoeffMat{{ - {1.00047e0,-1.02377e-3,-5.93877e-3,1.17393e-2,3.32021e-2,-5.38025e-3}, - {-4.05577e-1,8.21834e-1,1.22846e1,-1.96909e0,-7.68675e1,-6.12207e1}, - {3.08808e0,4.74434e0,-5.38383e1,-2.12945e1,3.99035e2,3.93158e2}, - {-1.93842e0,-4.37923e0,6.43816e1,4.83938e1,-5.3475e2,-5.92428e2}, - {5.28167e-2,-5.24544e-1,-2.39565e1,-2.29768e1,2.07685e2,2.48037e2} - }}, - //Tl................................................. - CoeffMat{{ - {1.00051e0,-9.77691e-4,-7.14416e-3,1.08563e-2,4.27196e-2,6.50699e-3}, - {-4.28882e-1,6.96049e-1,1.32126e1,-4.94649e-1,-8.48638e1,-7.26907e1}, - {3.08745e0,5.22659e0,-5.69235e1,-2.82201e1,4.30208e2,4.41611e2}, - {-1.82849e0,-4.60634e0,6.7553e1,5.68935e1,-5.69648e2,-6.49946e2}, - {-7.81506e-3,-5.82177e-1,-2.50201e1,-2.60444e1,2.19621e2,2.68607e2} - }}, - //Pb................................................. - CoeffMat{{ - {1.00055e0,-9.22411e-4,-8.38967e-3,9.83942e-3,5.26394e-2,1.91419e-2}, - {-4.49833e-1,5.63865e-1,1.41246e1,1.0768e0,-9.28737e1,-8.44177e1}, - {3.07189e0,5.71077e0,-5.98888e1,-3.54715e1,4.61066e2,4.9067e2}, - {-1.69869e0,-4.80968e0,7.05579e1,6.57062e1,-6.03887e2,-7.07793e2}, - {-7.55851e-2,-6.55871e-1,-2.60213e1,-2.92042e1,2.31249e2,2.89188e2} - }}, - //Bi................................................. - CoeffMat{{ - {1.00059e0,-8.58152e-4,-9.6602e-3,8.70229e-3,6.28328e-2,3.23376e-2}, - {-4.68192e-1,4.25668e-1,1.50174e1,2.74763e0,-1.00883e2,-9.64012e1}, - {3.04043e0,6.19366e0,-6.27207e1,-4.30487e1,4.91532e2,5.40291e2}, - {-1.54799e0,-4.98431e0,7.3381e1,7.48252e1,-6.37365e2,-7.65885e2}, - {-1.5081e-1,-7.47659e-1,-2.69551e1,-3.24524e1,2.42528e2,3.09744e2} - }}, - //Po................................................. - CoeffMat{{ - {1.00062e0,-7.84582e-4,-1.09553e-2,7.43533e-3,7.33106e-2,4.61358e-2}, - {-4.83646e-1,2.8205e-1,1.58842e1,4.51493e0,-1.08846e2,-1.08589e2}, - {2.99185e0,6.67128e0,-6.53938e1,-5.09329e1,5.21419e2,5.90246e2}, - {-1.37512e0,-5.12439e0,7.59949e1,8.42238e1,-6.69862e2,-8.23945e2}, - {-2.33878e-1,-8.59879e-1,-2.78125e1,-3.57783e1,2.53381e2,3.30173e2} - }}, - //At................................................. - CoeffMat{{ - {1.00066e0,-7.01484e-4,-1.22684e-2,6.03454e-3,8.40298e-2,6.05018e-2}, - {-4.95899e-1,1.3369e-1,1.67195e1,6.37574e0,-1.16723e2,-1.20936e2}, - {2.92499e0,7.13921e0,-6.78861e1,-5.91051e1,5.50562e2,6.40337e2}, - {-1.17889e0,-5.22361e0,7.8376e1,9.38748e1,-7.01185e2,-8.81728e2}, - {-3.25146e-1,-9.95032e-1,-2.85858e1,-3.91713e1,2.63738e2,3.50385e2} - }}, - //Rn................................................. - CoeffMat{{ - {1.0007e0,-6.08729e-4,-1.35873e-2,4.50061e-3,9.48991e-2,7.53277e-2}, - {-5.04647e-1,-1.86445e-2,1.75175e1,8.32729e0,-1.24477e2,-1.33405e2}, - {2.83871e0,7.59269e0,-7.01766e1,-6.75463e1,5.78809e2,6.90388e2}, - {-9.58143e-1,-5.27523e0,8.0502e1,1.03751e2,-7.31155e2,-9.39013e2}, - {-4.24964e-1,-1.15578e0,-2.92679e1,-4.26205e1,2.73537e2,3.70298e2} - }}, - //Fr................................................. - CoeffMat{{ - {1.00073e0,-5.06594e-4,-1.49195e-2,2.82752e-3,1.05984e-1,9.07179e-2}, - {-5.09498e-1,-1.74114e-1,1.82691e1,1.0364e1,-1.32042e2,-1.45912e2}, - {2.73154e0,8.02658e0,-7.22315e1,-7.62278e1,6.05902e2,7.40063e2}, - {-7.11399e-1,-5.27209e0,8.23375e1,1.13815e2,-7.59479e2,-9.95409e2}, - {-5.33773e-1,-1.34491e0,-2.98474e1,-4.61115e1,2.82675e2,3.89772e2} - }}, - //Ra................................................. - CoeffMat{{ - {1.00076e0,-3.94938e-4,-1.62461e-2,1.01616e-3,1.17156e-1,1.06529e-1}, - {-5.10179e-1,-3.3169e-1,1.89693e1,1.24811e1,-1.39382e2,-1.58412e2}, - {2.60247e0,8.43503e0,-7.4033e1,-8.51228e1,6.31692e2,7.89166e2}, - {-4.37673e-1,-5.2062e0,8.38642e1,1.2403e2,-7.85984e2,-1.05067e3}, - {-6.5185e-1,-1.56551e0,-3.03186e1,-4.96307e1,2.91091e2,4.08719e2} - }}, - //Ac................................................. - CoeffMat{{ - {1.00079e0,-2.74014e-4,-1.75614e-2,-9.33046e-4,1.28369e-1,1.22714e-1}, - {-5.06317e-1,-4.90282e-1,1.96102e1,1.46725e1,-1.4644e2,-1.70838e2}, - {2.45014e0,8.81187e0,-7.5553e1,-9.42005e1,6.55963e2,8.37419e2}, - {-1.35635e-1,-5.06921e0,8.50532e1,1.34356e2,-8.10425e2,-1.10448e3}, - {-7.79579e-1,-1.82077e0,-3.06725e1,-5.31628e1,2.987e2,4.2702e2} - }}, - //Th................................................ - CoeffMat{{ - {1.00082e0,-1.43895e-4,-1.8859e-2,-3.02404e-3,1.39581e-1,1.39236e-1}, - {-4.97537e-1,-6.48585e-1,2.01838e1,1.69296e1,-1.53157e2,-1.83108e2}, - {2.27321e0,9.15011e0,-7.6763e1,-1.0342e2,6.78484e2,8.84496e2}, - {1.9599e-1,-4.85189e0,8.5876e1,1.44742e2,-8.32543e2,-1.15645e3}, - {-9.17319e-1,-2.1142e0,-3.09003e1,-5.66889e1,3.05413e2,4.44542e2} - }}, - //Pa................................................. - CoeffMat{{ - {1.00085e0,-5.30411e-6,-2.01239e-2,-5.2478e-3,1.50683e-1,1.55963e-1}, - {-4.83526e-1,-8.05262e-1,2.06846e1,1.92461e1,-1.59496e2,-1.95177e2}, - {2.07059e0,9.44256e0,-7.76442e1,-1.12749e2,6.99103e2,9.30193e2}, - {5.58224e-1,-4.54471e0,8.63141e1,1.55146e2,-8.52163e2,-1.20633e3}, - {-1.06534e0,-2.44938e0,-3.09967e1,-6.01932e1,3.11168e2,4.61192e2} - }}, - //U.................................................... - CoeffMat{{ - {1.00087e0,1.42049e-4,-2.13455e-2,-7.61031e-3,1.61591e-1,1.72814e-1}, - {-4.6392e-1,-9.58725e-1,2.11056e1,2.16134e1,-1.65402e2,-2.06974e2}, - {1.841e0,9.68106e0,-7.81727e1,-1.22145e2,7.17614e2,9.74218e2}, - {9.52222e-1,-4.13715e0,8.63446e1,1.65515e2,-8.69054e2,-1.25378e3}, - {-1.22395e0,-2.83024e0,-3.09548e1,-6.36564e1,3.15887e2,4.76849e2} - }}}; - // clang-format on - static_assert(std::size(mott_coeffs) - == CoulombScatteringElementData::num_mott_elements, - "wrong number of Mott coefficient elements"); - - int index = z.unchecked_get() - 1; - CELER_VALIDATE( - index >= 0 - && index < int{CoulombScatteringElementData::num_mott_elements}, - << "atomic number " << z.get() - << " is out of range for Coulomb scattering model Mott coefficients " - "(must be less than " - << CoulombScatteringElementData::num_mott_elements << ")"); - - return mott_coeffs[index]; -} - -//---------------------------------------------------------------------------// -/*! - * Calculate the constant prefactors of the squared momentum transfer. - * - * This factor is used in the exponential and Gaussian nuclear form models: see - * Eqs. 2.262--2.264 of [LR15]. - * - * Specifically, it calculates \f$ (r_n/\bar h)^2 / 12 \f$. A special case is - * inherited from Geant for hydrogen targets. - */ -real_type -CoulombScatteringModel::calc_nuclear_form_prefactor(IsotopeView const& iso) -{ - if (iso.atomic_number().get() == 1) - { - // TODO: Geant4 hardcodes a different prefactor for hydrogen - return real_type{1.5485e-6}; - } - - // The ratio has units of (MeV/c)^-2, so it's easier to convert the - // inverse which has units of MomentumSq, then invert afterwards - constexpr real_type ratio - = 1 - / native_value_to( - 12 - * ipow<2>(constants::hbar_planck - / (real_type(1.27) * units::femtometer))) - .value(); - return ratio - * fastpow(real_type(iso.atomic_mass_number().get()), - 2 * real_type(0.27)); + return this->host_ref().action; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/em/model/CoulombScatteringModel.cu b/src/celeritas/em/model/CoulombScatteringModel.cu index 6cb08ffc1f..d6614b925a 100644 --- a/src/celeritas/em/model/CoulombScatteringModel.cu +++ b/src/celeritas/em/model/CoulombScatteringModel.cu @@ -8,6 +8,7 @@ #include "CoulombScatteringModel.hh" #include "celeritas/em/executor/CoulombScatteringExecutor.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/global/ActionLauncher.device.hh" #include "celeritas/global/CoreParams.hh" #include "celeritas/global/CoreState.hh" @@ -23,11 +24,14 @@ namespace celeritas void CoulombScatteringModel::execute(CoreParams const& params, CoreStateDevice& state) const { + CELER_EXPECT(params.wentzel()); + auto execute = make_action_track_executor( params.ptr(), state.ptr(), this->action_id(), - InteractionApplier{CoulombScatteringExecutor{this->device_ref()}}); + InteractionApplier{CoulombScatteringExecutor{ + this->device_ref(), params.wentzel()->device_ref()}}); static ActionLauncher const launch_kernel(*this); launch_kernel(state, execute); } diff --git a/src/celeritas/em/model/CoulombScatteringModel.hh b/src/celeritas/em/model/CoulombScatteringModel.hh index 236001c05b..af56db6706 100644 --- a/src/celeritas/em/model/CoulombScatteringModel.hh +++ b/src/celeritas/em/model/CoulombScatteringModel.hh @@ -9,11 +9,8 @@ #include -#include "corecel/data/CollectionMirror.hh" #include "celeritas/em/data/CoulombScatteringData.hh" -#include "celeritas/phys/AtomicNumber.hh" #include "celeritas/phys/ImportedModelAdapter.hh" -#include "celeritas/phys/ImportedProcessAdapter.hh" #include "celeritas/phys/Model.hh" namespace celeritas @@ -32,33 +29,12 @@ class CoulombScatteringModel final : public Model //!@{ //! \name Type aliases using SPConstImported = std::shared_ptr; - using HostRef = CoulombScatteringHostRef; - using DeviceRef = CoulombScatteringDeviceRef; //!@} - //! Wentzel Coulomb scattering model configuration options - struct Options - { - //! Nuclear form factor model - NuclearFormFactorType form_factor_model{ - NuclearFormFactorType::exponential}; - - //! User defined screening factor - real_type screening_factor{1}; - - //! Whether to use integral method to sample interaction length - bool use_integral_xs{true}; - - //! Check if the options are valid - explicit operator bool() const { return screening_factor > 0; } - }; - public: // Construct from model ID and other necessary data CoulombScatteringModel(ActionId id, ParticleParams const& particles, - MaterialParams const& materials, - Options const& options, SPConstImported data); // Particle types and energy ranges that this model applies to @@ -87,24 +63,13 @@ class CoulombScatteringModel final : public Model //!@{ //! Access model data - HostRef const& host_ref() const { return data_.host_ref(); } - DeviceRef const& device_ref() const { return data_.device_ref(); } + CoulombScatteringData const& host_ref() const { return data_; } + CoulombScatteringData const& device_ref() const { return data_; } //!@} private: - CollectionMirror data_; + CoulombScatteringData data_; ImportedModelAdapter imported_; - - // Construct per element data (loads Mott coefficients) - void build_data(HostVal& host_data, - MaterialParams const& materials); - - // Retrieve matrix of interpolated Mott coefficients - static CoulombScatteringElementData::MottCoeffMatrix - get_mott_coeff_matrix(AtomicNumber z); - - // Calculate the nuclear form prefactor - static real_type calc_nuclear_form_prefactor(IsotopeView const& iso); }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/em/params/UrbanMscParams.cc b/src/celeritas/em/params/UrbanMscParams.cc index 83256eb37d..214318ae9b 100644 --- a/src/celeritas/em/params/UrbanMscParams.cc +++ b/src/celeritas/em/params/UrbanMscParams.cc @@ -43,10 +43,7 @@ UrbanMscParams::from_import(ParticleParams const& particles, MaterialParams const& materials, ImportData const& data) { - auto is_urban = [](ImportMscModel const& imm) { - return imm.model_class == ImportModelClass::urban_msc; - }; - if (!std::any_of(data.msc_models.begin(), data.msc_models.end(), is_urban)) + if (!has_msc_model(data, ImportModelClass::urban_msc)) { // No Urban MSC present return nullptr; diff --git a/src/celeritas/em/params/WentzelOKVIParams.cc b/src/celeritas/em/params/WentzelOKVIParams.cc new file mode 100644 index 0000000000..f21043f116 --- /dev/null +++ b/src/celeritas/em/params/WentzelOKVIParams.cc @@ -0,0 +1,951 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/em/params/WentzelOKVIParams.cc +//---------------------------------------------------------------------------// +#include "WentzelOKVIParams.hh" + +#include +#include + +#include "corecel/io/Logger.hh" +#include "corecel/sys/ScopedMem.hh" +#include "celeritas/io/ImportData.hh" +#include "celeritas/mat/MaterialParams.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct if Wentzel VI or Coulomb is present, else return nullptr. + */ +std::shared_ptr +WentzelOKVIParams::from_import(ImportData const& data, + SPConstMaterials materials) +{ + CELER_EXPECT(materials); + + bool wentzel = has_msc_model(data, ImportModelClass::wentzel_vi_uni); + bool coulomb = has_model(data, ImportModelClass::e_coulomb_scattering); + if (!(wentzel || coulomb)) + { + // No Wentzel VI MSC or Coulomb scattering present + return nullptr; + } + + Options opts; + opts.is_combined = wentzel && coulomb; + opts.polar_angle_limit = [&]() -> real_type { + if (!coulomb) + { + // Set the maximum scattering angle for Wentzel VI MSC + return constants::pi; + } + if (!wentzel) + { + // Set the minimum scattering angle for Coulomb single scattering + return real_type(0); + } + // Polar angle limit between single and multiple scattering if both + // models are present + return data.em_params.msc_theta_limit; + }(); + opts.screening_factor = data.em_params.screening_factor; + opts.angle_limit_factor = data.em_params.angle_limit_factor; + opts.form_factor = data.em_params.form_factor; + + return std::make_shared(materials, opts); +} + +//---------------------------------------------------------------------------// +/*! + * Construct from cross section data and material properties. + */ +WentzelOKVIParams::WentzelOKVIParams(SPConstMaterials materials, + Options options) +{ + CELER_EXPECT(materials); + + ScopedMem record_mem("WentzelOKVIParams.construct"); + + HostVal host_data; + + host_data.params.is_combined = options.is_combined; + host_data.params.costheta_limit = std::cos(options.polar_angle_limit); + host_data.params.a_sq_factor + = real_type(0.5) + * ipow<2>(options.angle_limit_factor * constants::hbar_planck + * constants::c_light * units::femtometer); + host_data.params.screening_factor = options.screening_factor; + host_data.params.form_factor_type = options.form_factor; + + // Load Mott coefficients + build_data(host_data, *materials); + CELER_ASSERT(host_data); + + // Move to mirrored data, copying to device + data_ = CollectionMirror{std::move(host_data)}; + CELER_ENSURE(data_); +} + +//---------------------------------------------------------------------------// +/*! + * Load Mott coefficients and construct per-element data. + */ +void WentzelOKVIParams::build_data(HostVal& host_data, + MaterialParams const& materials) +{ + // Build element data + size_type const num_elements = materials.num_elements(); + auto elem_data = make_builder(&host_data.elem_data); + elem_data.reserve(num_elements); + + for (auto el_id : range(ElementId{num_elements})) + { + // Load Mott coefficients + MottElementData z_data; + z_data.mott_coeff + = get_mott_coeff_matrix(materials.get(el_id).atomic_number()); + elem_data.push_back(z_data); + } + + auto prefactors = make_builder(&host_data.nuclear_form_prefactor); + prefactors.reserve(materials.num_isotopes()); + for (auto iso_id : range(IsotopeId{materials.num_isotopes()})) + { + prefactors.push_back( + this->calc_nuclear_form_prefactor(materials.get(iso_id))); + } + CELER_ENSURE(host_data.nuclear_form_prefactor.size() + == materials.num_isotopes()); + + // Build material data + if (host_data.params.is_combined) + { + std::vector inv_mass_cbrt_sq(materials.num_materials(), 0); + for (auto mat_id : range(MaterialId(materials.num_materials()))) + { + auto mat = materials.get(mat_id); + for (auto elcomp_id : range(ElementComponentId(mat.num_elements()))) + { + auto const& el_comp = mat.elements()[elcomp_id.get()]; + auto atomic_mass + = mat.make_element_view(elcomp_id).atomic_mass(); + inv_mass_cbrt_sq[mat_id.get()] + += el_comp.fraction + / std::pow(atomic_mass.value(), real_type(2) / 3); + } + inv_mass_cbrt_sq[mat_id.get()] *= mat.number_density(); + } + make_builder(&host_data.inv_mass_cbrt_sq) + .insert_back(inv_mass_cbrt_sq.begin(), inv_mass_cbrt_sq.end()); + } +} + +//---------------------------------------------------------------------------// +/*! + * Calculate the constant prefactors of the squared momentum transfer. + * + * This factor is used in the exponential and Gaussian nuclear form factor + * types: see Eqs. 2.262--2.264 of [LR15]. + * + * Specifically, it calculates \f$ (r_n/\bar h)^2 / 12 \f$, where \f$ r_n = + * 1.27 A^{0.27} \f$ [fm] is the nuclear radius. A special case is inherited + * from Geant for hydrogen targets. + */ +real_type WentzelOKVIParams::calc_nuclear_form_prefactor(IsotopeView const& iso) +{ + if (iso.atomic_number().get() == 1) + { + // TODO: Geant4 hardcodes a different prefactor for hydrogen + return real_type{1.5485e-6}; + } + + // The ratio has units of (MeV/c)^-2, so it's easier to convert the + // inverse which has units of MomentumSq, then invert afterwards + constexpr real_type ratio + = 1 + / native_value_to( + 12 + * ipow<2>(constants::hbar_planck + / (real_type(1.27) * units::femtometer))) + .value(); + return ratio + * fastpow(real_type(iso.atomic_mass_number().get()), + 2 * real_type(0.27)); +} + +//---------------------------------------------------------------------------// +/*! + * Get interpolated Mott coefficients for an element. + * + * These coefficients are used by the Lijian, Quing, Zhengming + * expression [PRM 8.48] for atomic numbers 1 <= Z <= 92. + * This data was taken from Geant4's G4MottData.hh file. + * + * For higher Z values, the PRM cites a numerical solution by Boschini + * et al (2013), but does not implement it. + */ +MottElementData::MottCoeffMatrix +WentzelOKVIParams::get_mott_coeff_matrix(AtomicNumber z) +{ + CELER_EXPECT(z); + + using CoeffMat = MottElementData::MottCoeffMatrix; + // clang-format off + static CoeffMat const mott_coeffs[] = { + //H....................................... + CoeffMat{{ + {1.e0,2.67363e-8,7.1153e-8,-9.7703e-8,-6.69132e-7,-3.09263e-7}, + {1.17182e-2,1.62222e-2,-5.90397e-5,-1.05585e-4,4.17873e-4,9.13843e-4}, + {-2.65955e-1,-7.29531e-1,-4.99796e-1,2.83507e-4,-9.09042e-4,-2.20244e-3}, + {-1.82348e-4,-8.86355e-5,-1.90554e-4,-2.49708e-4,6.35004e-4,1.73523e-3}, + {4.70966e-5,-4.09705e-6,3.75218e-5,8.05645e-5,-1.90534e-4,-5.42847e-4} + }}, + //He....................................... + CoeffMat{{ + {1.e0,3.76476e-8,-3.05313e-7,-3.27422e-7,2.44235e-6,4.08754e-6}, + {2.35767e-2,3.24642e-2,-6.37269e-4,-7.6916e-4,5.28004e-3,9.45642e-3}, + {-2.73743e-1,-7.40767e-1,-4.98195e-1,1.74337e-3,-1.25798e-2,-2.24046e-2}, + {-7.79128e-4,-4.14495e-4,-1.62657e-3,-1.37286e-3,1.04319e-2,1.83488e-2}, + {2.02855e-4,1.94598e-6,4.30102e-4,4.3218e-4,-3.31526e-3,-5.81788e-3} + }}, + //Li.................................................. + CoeffMat{{ + {1.e0,7.00357e-8,-3.15076e-7,-4.24915e-7,2.45516e-6,4.90187e-6}, + {3.55657e-2,4.87956e-2,-1.95525e-3,-2.7866e-3,1.6549e-2,3.11496e-2}, + {-2.81171e-1,-7.52015e-1,-4.95329e-1,5.83548e-3,-3.3983e-2,-6.55379e-2}, + {-1.83452e-3,-8.12746e-4,-3.84675e-3,-4.44467e-3,2.55871e-2,4.99483e-2}, + {4.79031e-4,-3.89615e-5,1.01022e-3,1.39133e-3,-7.99398e-3,-1.56366e-2} + }}, + //Be.................................................. + CoeffMat{{ + {1.e0,7.58881e-8,4.705e-8,2.48041e-7,-2.06053e-6,-1.97319e-6}, + {4.76788e-2,6.522e-2,-4.54331e-3,-6.50318e-3,3.76564e-2,7.17176e-2}, + {-2.88203e-1,-7.63217e-1,-4.90337e-1,1.22839e-2,-6.86398e-2,-1.35769e-1}, + {-3.37733e-3,-1.36514e-3,-7.51614e-3,-8.78592e-3,4.78572e-2,9.69021e-2}, + {8.81822e-4,-1.02577e-4,1.99797e-3,2.72661e-3,-1.48296e-2,-3.0106e-2} + }}, + //B.................................................... + CoeffMat{{ + {9.99999e-1,7.91498e-8,1.84164e-6,2.68534e-6,-1.8163e-5,-2.69021e-5}, + {5.98818e-2,8.17654e-2,-7.70811e-3,-1.12378e-2,6.38329e-2,1.25339e-1}, + {-2.94716e-1,-7.74405e-1,-4.8622e-1,1.77367e-2,-9.46825e-2,-2.01789e-1}, + {-5.52375e-3,-2.05348e-3,-9.44915e-3,-1.08135e-2,5.41024e-2,1.25257e-1}, + {1.44555e-3,-1.99404e-4,2.36742e-3,3.29655e-3,-1.64122e-2,-3.8375e-2} + }}, + //C................................................... + CoeffMat{{ + {9.99999e-1,7.68158e-8,5.18185e-6,7.34245e-6,-4.9478e-5,-7.71923e-5}, + {7.21461e-2,9.84618e-2,-1.06535e-2,-1.62358e-2,8.59238e-2,1.78727e-1}, + {-3.00622e-1,-7.85616e-1,-4.85735e-1,1.91563e-2,-8.10204e-2,-2.15386e-1}, + {-8.34809e-3,-2.85241e-3,-7.03252e-3,-7.56786e-3,1.44975e-2,8.79093e-2}, + {2.18964e-3,-3.42022e-4,1.3293e-3,2.20108e-3,-3.57927e-3,-2.5928e-2} + }}, + //N.................................................... + CoeffMat{{ + {9.99999e-1,8.36312e-8,1.09116e-5,1.47812e-5,-1.02733e-4,-1.62724e-4}, + {8.44142e-2,1.1531e-1,-1.1723e-2,-1.94732e-2,8.92604e-2,2.09303e-1}, + {-3.05743e-1,-7.96809e-1,-4.93957e-1,1.01607e-2,1.67761e-2,-1.05909e-1}, + {-1.2009e-2,-3.80678e-3,4.51195e-3,6.93472e-3,-1.12405e-1,-8.15484e-2}, + {3.16048e-3,-5.22237e-4,-2.58261e-3,-2.38303e-3,3.63393e-2,2.75127e-2} + }}, + //O................................................... + CoeffMat{{ + {9.99998e-1,1.57323e-8,1.77595e-5,2.56082e-5,-1.67537e-4,-2.73755e-4}, + {9.66438e-2,1.32264e-1,-9.53841e-3,-1.83707e-2,6.01664e-2,1.93357e-1}, + {-3.09969e-1,-8.0779e-1,-5.14392e-1,-1.67153e-2,2.3387e-1,1.916e-1}, + {-1.65906e-2,-5.11585e-3,2.80424e-2,3.94663e-2,-3.5572e-1,-4.39251e-1}, + {4.37866e-3,-6.81795e-4,-1.0152e-2,-1.24875e-2,1.11484e-1,1.38105e-1} + }}, + //F................................................. + CoeffMat{{ + {9.99997e-1,-8.06132e-8,2.49797e-5,3.8512e-5,-2.37451e-4,-3.99607e-4}, + {1.08782e-1,1.49306e-1,-2.50975e-3,-1.05471e-2,-1.64831e-2,1.05733e-1}, + {-3.13165e-1,-8.18489e-1,-5.50832e-1,-6.74447e-2,6.06357e-1,7.39717e-1}, + {-2.21976e-2,-6.84023e-3,6.65411e-2,9.48702e-2,-7.43989e-1,-1.03582e0}, + {5.87088e-3,-8.1048e-4,-2.21731e-2,-2.94422e-2,2.2954e-1,3.19669e-1} + }}, + //Ne................................................. + CoeffMat{{ + {9.99997e-1,-1.87404e-7,3.10276e-5,5.20007e-5,-2.98132e-4,-5.19259e-4}, + {1.20783e-1,1.66407e-1,1.06608e-2,6.48772e-3,-1.53031e-1,-7.59354e-2}, + {-3.15222e-1,-8.28793e-1,-6.0574e-1,-1.47812e-1,1.1576e0,1.58565e0}, + {-2.89055e-2,-9.08096e-3,1.21467e-1,1.77575e-1,-1.2911e0,-1.90333e0}, + {7.65342e-3,-8.85417e-4,-3.89092e-2,-5.4404e-2,3.93087e-1,5.79439e-1} + }}, + //Na................................................. + CoeffMat{{ + {9.99996e-1,-2.44548e-7,3.31019e-5,6.29483e-5,-3.24667e-4,-5.95527e-4}, + {1.32615e-1,1.83566e-1,3.04158e-2,3.40925e-2,-3.54681e-1,-3.63044e-1}, + {-3.16092e-1,-8.38704e-1,-6.78558e-1,-2.59346e-1,1.88547e0,2.73632e0}, + {-3.67233e-2,-1.18139e-2,1.91089e-1,2.87408e-1,-1.98397e0,-3.03075e0}, + {9.72033e-3,-9.2638e-4,-5.95654e-2,-8.69829e-2,5.95744e-1,9.10242e-1} + }}, + //Mg................................................. + CoeffMat{{ + {9.99995e-1,-2.12227e-7,2.95645e-5,6.92848e-5,-3.02153e-4,-6.05145e-4}, + {1.44258e-1,2.00775e-1,5.67845e-2,7.35166e-2,-6.22861e-1,-7.62213e-1}, + {-3.15754e-1,-8.48196e-1,-7.67318e-1,-4.02984e-1,2.77477e0,4.18114e0}, + {-4.56307e-2,-1.50425e-2,2.72232e-1,4.23528e-1,-2.79606e0,-4.38863e0}, + {1.2056e-2,-9.44637e-4,-8.28738e-2,-1.26564e-1,8.26726e-1,1.29882e0} + }}, + //Al..................................................... + CoeffMat{{ + {9.99995e-1,-4.03407e-8,1.86047e-5,6.85201e-5,-2.14503e-4,-5.22528e-4}, + {1.55704e-1,2.18048e-1,8.88994e-2,1.24878e-1,-9.51331e-1,-1.26824e0}, + {-3.14244e-1,-8.57322e-1,-8.6719e-1,-5.75787e-1,3.78571e0,5.87052e0}, + {-5.55526e-2,-1.86861e-2,3.5886e-1,5.81094e-1,-3.67623e0,-5.908e0}, + {1.46269e-2,-9.79742e-4,-1.06652e-1,-1.71226e-1,1.06737e0,1.71918e0} + }}, + //Si..................................................... + CoeffMat{{ + {9.99994e-1,3.00267e-7,-1.1184e-6,5.88256e-5,-4.78456e-5,-3.25731e-4}, + {1.6696e-1,2.35405e-1,1.25215e-1,1.87646e-1,-1.32685e0,-1.86549e0}, + {-3.1163e-1,-8.66152e-1,-9.71254e-1,-7.72715e-1,4.85654e0,7.7215e0}, + {-6.63778e-2,-2.26481e-2,4.42898e-1,7.53182e-1,-4.55172e0,-7.4867e0}, + {1.73883e-2,-1.07669e-3,-1.28075e-1,-2.18389e-1,1.29217e0,2.13475e0} + }}, + //P..................................................... + CoeffMat{{ + {9.99994e-1,8.94829e-7,-2.98434e-5,3.82193e-5,2.00584e-4,-6.40482e-6}, + {1.78039e-1,2.52912e-1,1.63761e-1,2.60132e-1,-1.73287e0,-2.53185e0}, + {-3.08007e-1,-8.74905e-1,-1.07146e0,-9.85062e-1,5.91697e0,9.63265e0}, + {-7.79747e-2,-2.66797e-2,5.15288e-1,9.29261e-1,-5.34252e0,-9.00574e0}, + {2.02892e-2,-1.33011e-3,-1.44039e-1,-2.6433e-1,1.4736e0,2.50398e0} + }}, + //S.................................................... + CoeffMat{{ + {9.99994e-1,1.75397e-6,-6.7331e-5,6.29524e-6,5.29623e-4,4.35288e-4}, + {1.88968e-1,2.70612e-1,2.01975e-1,3.40574e-1,-2.14737e0,-3.23836e0}, + {-3.03499e-1,-8.83717e-1,-1.15816e0,-1.20414e0,6.88176e0,1.14841e1}, + {-9.01806e-2,-3.06202e-2,5.65581e-1,1.09902e0,-5.95552e0,-1.03302e1}, + {2.32694e-2,-1.80614e-3,-1.51041e-1,-3.05449e-1,1.58037e0,2.78083e0} + }}, + //Cl.................................................... + CoeffMat{{ + {9.99994e-1,3.07931e-6,-1.11876e-4,-4.10164e-5,9.17095e-4,9.80145e-4}, + {1.99765e-1,2.88611e-1,2.37501e-1,4.25803e-1,-2.55105e0,-3.95585e0}, + {-2.98206e-1,-8.9293e-1,-1.22279e0,-1.4169e0,7.67836e0,1.31601e1}, + {-1.02865e-1,-3.40967e-2,5.84677e-1,1.24786e0,-6.31301e0,-1.13328e1}, + {2.628e-2,-2.63995e-3,-1.46076e-1,-3.36795e-1,1.58677e0,2.92251e0} + }}, + //Ar.................................................... + CoeffMat{{ + {9.99993e-1,4.49776e-6,-1.65136e-4,-9.76754e-5,1.39664e-3,1.66293e-3}, + {2.10469e-1,3.06924e-1,2.66793e-1,5.13797e-1,-2.90958e0,-4.63816e0}, + {-2.92294e-1,-9.0256e-1,-1.25307e0,-1.6147e0,8.18574e0,1.44912e1}, + {-1.15831e-1,-3.70891e-2,5.59807e-1,1.36619e0,-6.28824e0,-1.18327e1}, + {2.92513e-2,-3.84903e-3,-1.24976e-1,-3.55149e-1,1.45127e0,2.86925e0} + }}, + //K................................................. + CoeffMat{{ + {9.99993e-1,6.01488e-6,-2.22125e-4,-1.61774e-4,1.92058e-3,2.41975e-3}, + {2.21091e-1,3.25648e-1,2.87732e-1,6.01632e-1,-3.20436e0,-5.25724e0}, + {-2.85814e-1,-9.12889e-1,-1.24213e0,-1.78635e0,8.34196e0,1.53776e1}, + {-1.29005e-1,-3.92986e-2,4.84255e-1,1.44206e0,-5.81999e0,-1.17275e1}, + {3.21555e-2,-5.54272e-3,-8.56572e-2,-3.56589e-1,1.1548e0,2.58829e0} + }}, + //Ca................................................. + CoeffMat{{ + {9.99993e-1,8.01467e-6,-2.79242e-4,-2.3682e-4,2.45459e-3,3.21683e-3}, + {2.31651e-1,3.44948e-1,2.9782e-1,6.85187e-1,-3.41294e0,-5.77715e0}, + {-2.78858e-1,-9.24428e-1,-1.18215e0,-1.91691e0,8.07489e0,1.56969e1}, + {-1.42276e-1,-4.01888e-2,3.50466e-1,1.45983e0,-4.83806e0,-1.08936e1}, + {3.49529e-2,-7.90933e-3,-2.58002e-2,-3.36028e-1,6.7574e-1,2.04052e0} + }}, + //Sc................................................. + CoeffMat{{ + {9.99992e-1,1.04277e-5,-3.35126e-4,-3.21042e-4,2.98507e-3,4.03325e-3}, + {2.42172e-1,3.64954e-1,2.94606e-1,7.60693e-1,-3.51409e0,-6.1646e0}, + {-2.71512e-1,-9.37543e-1,-1.0657e0,-1.99328e0,7.31863e0,1.53396e1}, + {-1.5554e-1,-3.93862e-2,1.51477e-1,1.40614e0,-3.28024e0,-9.22338e0}, + {3.76066e-2,-1.10812e-2,5.66831e-2,-2.8921e-1,-4.60274e-3,1.19273e0} + }}, + //Ti................................................. + CoeffMat{{ + {9.99992e-1,1.30838e-5,-3.8407e-4,-4.09294e-4,3.47025e-3,4.81071e-3}, + {2.52646e-1,3.85718e-1,2.7633e-1,8.25665e-1,-3.48888e0,-6.39017e0}, + {-2.63758e-1,-9.52326e-1,-8.88179e-1,-2.0072e0,6.0196e0,1.42157e1}, + {-1.68806e-1,-3.68095e-2,-1.16429e-1,1.27309e0,-1.09991e0,-6.63409e0}, + {4.01184e-2,-1.50938e-2,1.6274e-1,-2.1376e-1,-8.99142e-1,2.08129e-2} + }}, + //V................................................. + CoeffMat{{ + {9.99991e-1,1.59363e-5,-4.27315e-4,-5.01461e-4,3.91365e-3,5.55096e-3}, + {2.63096e-1,4.07357e-1,2.40649e-1,8.7645e-1,-3.31641e0,-6.42069e0}, + {-2.55682e-1,-9.6909e-1,-6.43149e-1,-1.94678e0,4.11915e0,1.22255e1}, + {-1.81974e-1,-3.21462e-2,-4.58817e-1,1.04913e0,1.75388e0,-3.03434e0}, + {4.24541e-2,-2.00595e-2,2.93916e-1,-1.06138e-1,-2.02203e0,-1.50181e0} + }}, + //Cr................................................. + CoeffMat{{ + {9.9999e-1,1.8895e-5,-4.59994e-4,-5.93663e-4,4.27684e-3,6.2009e-3}, + {2.73504e-1,4.2999e-1,1.86473e-1,9.09921e-1,-2.98441e0,-6.23344e0}, + {-2.47225e-1,-9.88118e-1,-3.28759e-1,-1.80252e0,1.5909e0,9.30968e0}, + {-1.951e-1,-2.51242e-2,-8.76244e-1,7.25592e-1,5.2965e0,1.62177e0}, + {4.46307e-2,-2.60754e-2,4.50054e-1,3.61638e-2,-3.37524e0,-3.38601e0} + }}, + //Mn................................................. + CoeffMat{{ + {9.99989e-1,2.18906e-5,-4.79199e-4,-6.83164e-4,4.53467e-3,6.72485e-3}, + {2.83854e-1,4.53722e-1,1.12877e-1,9.23203e-1,-2.48247e0,-5.80855e0}, + {-2.38338e-1,-1.00965e0,5.61453e-2,-1.56605e0,-1.58353e0,5.42138e0}, + {-2.08226e-1,-1.55138e-2,-1.36846e0,2.95168e-1,9.53392e0,7.3655e0}, + {4.66619e-2,-3.32255e-2,6.30711e-1,2.15167e-1,-4.95748e0,-5.63744e0} + }}, + //Fe................................................... + CoeffMat{{ + {9.99987e-1,2.482e-5,-4.82895e-4,-7.67488e-4,4.669e-3,7.09581e-3}, + {2.94123e-1,4.78653e-1,1.93256e-2,9.13569e-1,-1.80354e0,-5.1304e0}, + {-2.28945e-1,-1.0339e0,5.1121e-1,-1.22996e0,-5.40939e0,5.3149e-1}, + {-2.21426e-1,-3.12679e-3,-1.93353e0,-2.48179e-1,1.44576e1,1.42077e1}, + {4.85718e-2,-4.15796e-2,8.34879e-1,4.32418e-1,-6.76244e0,-8.25464e0} + }}, + //Co.................................................. + CoeffMat{{ + {9.99985e-1,2.76168e-5,-4.67522e-4,-8.43978e-4,4.65008e-3,7.27476e-3}, + {3.0428e-1,5.04856e-1,-9.42256e-2,8.78905e-1,-9.44256e-1,-4.18847e0}, + {-2.18939e-1,-1.06098e0,1.03431e0,-7.89123e-1,-9.87815e0,-5.36997e0}, + {-2.34805e-1,1.21281e-2,-2.56765e0,-9.08015e-1,2.00436e1,2.21379e1}, + {5.03942e-2,-5.11768e-2,1.0609e0,6.88631e-1,-8.77867e0,-1.12289e1} + }}, + //Ni.................................................. + CoeffMat{{ + {9.99982e-1,3.00792e-5,-4.33447e-4,-9.09366e-4,4.47786e-3,7.25072e-3}, + {3.14283e-1,5.32444e-1,-2.27528e-1,8.16951e-1,9.53704e-2,-2.9773e0}, + {-2.08186e-1,-1.09112e0,1.62237e0,-2.38394e-1,-1.49699e1,-1.22743e1}, + {-2.48493e-1,3.04543e-2,-3.26601e0,-1.6876e0,2.62568e1,3.11259e1}, + {5.21709e-2,-6.20908e-2,1.30683e0,9.84353e-1,-1.09909e1,-1.45449e1} + }}, + //Cu................................................... + CoeffMat{{ + {9.99979e-1,3.24569e-5,-3.76717e-4,-9.64e-4,4.11997e-3,6.98761e-3}, + {3.24082e-1,5.61534e-1,-3.79993e-1,7.25313e-1,1.31342e0,-1.49163e0}, + {-1.96521e-1,-1.12457e0,2.27091e0,4.27678e-1,-2.06558e1,-2.0169e1}, + {-2.62655e-1,5.20812e-2,-4.02224e0,-2.59048e0,3.30508e1,4.1135e1}, + {5.39556e-2,-7.44077e-2,1.57015e0,1.32022e0,-1.33802e1,-1.81848e1} + }}, + //Zn................................................... + CoeffMat{{ + {9.99976e-1,3.39628e-5,-2.98845e-4,-9.99651e-4,3.58523e-3,6.47782e-3}, + {3.33647e-1,5.92068e-1,-5.5102e-1,6.0363e-1,2.70712e0,2.67853e-1}, + {-1.83849e-1,-1.16095e0,2.97559e0,1.20728e0,-2.69053e1,-2.90214e1}, + {-2.77375e-1,7.65838e-2,-4.83023e0,-3.61238e0,4.03786e1,5.21084e1}, + {5.57745e-2,-8.79952e-2,1.84848e0,1.69425e0,-1.59274e1,-2.21242e1} + }}, + //Ga................................................... + CoeffMat{{ + {9.99972e-1,3.48473e-5,-1.97828e-4,-1.01659e-3,2.85509e-3,5.69725e-3}, + {3.42904e-1,6.24164e-1,-7.39023e-1,4.50306e-1,4.26553e0,2.29299e0}, + {-1.6994e-1,-1.20051e0,3.72868e0,2.10276e0,-3.36594e1,-3.87726e1}, + {-2.92882e-1,1.04194e-1,-5.68033e0,-4.75339e0,4.81633e1,6.39614e1}, + {5.77008e-2,-1.02941e-1,2.13826e0,2.10589e0,-1.86034e1,-2.63294e1} + }}, + //Ge................................................. + CoeffMat{{ + {9.99968e-1,3.47804e-5,-7.11898e-5,-1.01028e-3,1.90919e-3,4.61426e-3}, + {3.51793e-1,6.57815e-1,-9.42175e-1,2.6494e-1,5.97606e0,4.57222e0}, + {-1.54595e-1,-1.24305e0,4.5217e0,3.11205e0,-4.08539e1,-4.93518e1}, + {-3.09367e-1,1.34661e-1,-6.56214e0,-6.00882e0,5.63228e1,7.65965e1}, + {5.97948e-2,-1.19174e-1,2.4357e0,2.553e0,-2.13779e1,-3.07628e1} + }}, + //As................................................. + CoeffMat{{ + {9.99963e-1,3.37519e-5,8.13037e-5,-9.78638e-4,7.41412e-4,3.21498e-3}, + {3.60246e-1,6.93065e-1,-1.1588e0,4.68519e-2,7.82662e0,7.09456e0}, + {-1.37615e-1,-1.28855e0,5.34673e0,4.23402e0,-4.84263e1,-6.06897e1}, + {-3.27017e-1,1.67941e-1,-7.46593e0,-7.37492e0,6.47773e1,8.99181e1}, + {6.21158e-2,-1.36692e-1,2.73726e0,3.03374e0,-2.42209e1,-3.53873e1} + }}, + //Se................................................. + CoeffMat{{ + {9.99958e-1,3.14983e-5,2.59741e-4,-9.19008e-4,-6.49202e-4,1.49008e-3}, + {3.68196e-1,7.29888e-1,-1.38664e0,-2.03807e-1,9.80062e0,9.84155e0}, + {-1.18788e-1,-1.33674e0,6.194e0,5.46446e0,-5.62994e1,-7.26915e1}, + {-3.46033e-1,2.03729e-1,-8.38012e0,-8.84466e0,7.34319e1,1.03805e2}, + {6.47255e-2,-1.55409e-1,3.03879e0,3.54516e0,-2.7098e1,-4.01572e1} + }}, + //Br................................................. + CoeffMat{{ + {9.99952e-1,2.79961e-5,4.60479e-4,-8.33486e-4,-2.23214e-3,-5.16285e-4}, + {3.7558e-1,7.68292e-1,-1.62392e0,-4.87674e-1,1.18854e1,1.28025e1}, + {-9.79355e-2,-1.38749e0,7.0556e0,6.80198e0,-6.44113e1,-8.52915e1}, + {-3.66572e-1,2.41863e-1,-9.29524e0,-1.04141e1,8.22093e1,1.18166e2}, + {6.76714e-2,-1.75289e-1,3.33691e0,4.08538e0,-2.99809e1,-4.50381e1} + }}, + //Kr................................................. + CoeffMat{{ + {9.99947e-1,2.2952e-5,6.82639e-4,-7.17139e-4,-4.00522e-3,-2.81601e-3}, + {3.82332e-1,8.08194e-1,-1.86848e0,-8.03415e-1,1.4064e1,1.5956e1}, + {-7.48735e-2,-1.44031e0,7.92233e0,8.2382e0,-7.26858e1,-9.83865e1}, + {-3.88797e-1,2.81837e-1,-1.02005e1,-1.20716e1,9.10175e1,1.32873e2}, + {7.10017e-2,-1.96182e-1,3.6278e0,4.65e0,-3.28365e1,-4.99823e1} + }}, + //Rb................................................. + CoeffMat{{ + {9.99941e-1,1.63607e-5,9.28242e-4,-5.69257e-4,-5.98245e-3,-5.42476e-3}, + {3.88363e-1,8.49548e-1,-2.11701e0,-1.15003e0,1.63119e1,1.92737e1}, + {-4.93364e-2,-1.49491e0,8.78129e0,9.76596e0,-8.10212e1,-1.11851e2}, + {-4.12953e-1,3.23322e-1,-1.10814e1,-1.38073e1,9.97386e1,1.47775e2}, + {7.47911e-2,-2.18001e-1,3.90642e0,5.23509e0,-3.56231e1,-5.49352e1} + }}, + //Sr................................................. + CoeffMat{{ + {9.99935e-1,8.3152e-6,1.19244e-3,-3.95258e-4,-8.12525e-3,-8.28836e-3}, + {3.93603e-1,8.92358e-1,-2.36688e0,-1.52786e0,1.86071e1,2.27314e1}, + {-2.11345e-2,-1.55112e0,9.62205e0,1.13827e1,-8.93266e1,-1.25574e2}, + {-4.39199e-1,3.66161e-1,-1.19262e1,-1.56159e1,1.08267e2,1.62736e2}, + {7.90853e-2,-2.40719e-1,4.16872e0,5.83839e0,-3.8304e1,-5.98484e1} + }}, + //Y................................................. + CoeffMat{{ + {9.99929e-1,-1.65608e-6,1.47476e-3,-1.8625e-4,-1.04363e-2,-1.14318e-2}, + {3.97989e-1,9.3643e-1,-2.61569e0,-1.93387e0,2.09305e1,2.63021e1}, + {9.88744e-3,-1.60813e0,1.04351e1,1.3074e1,-9.75207e1,-1.39437e2}, + {-4.67657e-1,4.09494e-1,-1.2724e1,-1.74797e1,1.16508e2,1.77614e2}, + {8.39169e-2,-2.64077e-1,4.41092e0,6.45344e0,-4.08455e1,-6.46698e1} + }}, + //Zr................................................. + CoeffMat{{ + {9.99922e-1,-1.37624e-5,1.77335e-3,5.994e-5,-1.29013e-2,-1.48443e-2}, + {4.0145e-1,9.816e-1,-2.86053e0,-2.36562e0,2.32591e1,2.99551e1}, + {4.3915e-2,-1.66522e0,1.12093e1,1.48279e1,-1.05512e2,-1.5331e2}, + {-4.98475e-1,4.52607e-1,-1.34628e1,-1.93836e1,1.24357e2,1.92256e2}, + {8.93258e-2,-2.87869e-1,4.62891e0,7.07473e0,-4.32116e1,-6.93461e1} + }}, + //Nb................................................. + CoeffMat{{ + {9.99916e-1,-2.78975e-5,2.08753e-3,3.41607e-4,-1.55141e-2,-1.85156e-2}, + {4.03889e-1,1.02783e0,-3.09762e0,-2.82183e0,2.55629e1,3.36548e1}, + {8.12122e-2,-1.7221e0,1.1931e1,1.66362e1,-1.13185e2,-1.67048e2}, + {-5.3188e-1,4.95236e-1,-1.41275e1,-2.13166e1,1.31687e2,2.06495e2}, + {9.53781e-2,-3.12041e-1,4.81765e0,7.69813e0,-4.53587e1,-7.38182e1} + }}, + //Mo................................................. + CoeffMat{{ + {9.9991e-1,-4.3988e-5,2.41439e-3,6.54636e-4,-1.82489e-2,-2.24062e-2}, + {4.05249e-1,1.07495e0,-3.32445e0,-3.30073e0,2.78221e1,3.7376e1}, + {1.21896e-1,-1.77813e0,1.25907e1,1.84893e1,-1.20461e2,-1.80542e2}, + {-5.67942e-1,5.36741e-1,-1.4708e1,-2.32664e1,1.38408e2,2.20203e2}, + {1.02086e-1,-3.36419e-1,4.97373e0,8.31905e0,-4.72562e1,-7.80409e1} + }}, + //Tc................................................. + CoeffMat{{ + {9.99904e-1,-6.22073e-5,2.75066e-3,1.00063e-3,-2.10817e-2,-2.64933e-2}, + {4.05472e-1,1.12279e0,-3.53825e0,-3.79964e0,3.00132e1,4.10871e1}, + {1.66089e-1,-1.83255e0,1.31785e1,2.03743e1,-1.27252e2,-1.93664e2}, + {-6.06729e-1,5.76392e-1,-1.51936e1,-2.52171e1,1.44423e2,2.33232e2}, + {1.09463e-1,-3.60803e-1,5.09358e0,8.93178e0,-4.88706e1,-8.19632e1} + }}, + //Ru................................................. + CoeffMat{{ + {9.99898e-1,-8.26232e-5,3.09353e-3,1.37924e-3,-2.39902e-2,-3.07502e-2}, + {4.04498e-1,1.17115e0,-3.73628e0,-4.3158e0,3.21142e1,4.4758e1}, + {2.13904e-1,-1.88458e0,1.36848e1,2.22782e1,-1.33471e2,-2.06293e2}, + {-6.48301e-1,6.13439e-1,-1.55742e1,-2.71531e1,1.49638e2,2.45444e2}, + {1.17516e-1,-3.8499e-1,5.17383e0,9.5307e0,-5.01709e1,-8.55368e1} + }}, + //Rh................................................. + CoeffMat{{ + {9.99893e-1,-1.05293e-4,3.43932e-3,1.78981e-3,-2.69462e-2,-3.51436e-2}, + {4.02272e-1,1.21982e0,-3.91575e0,-4.84621e0,3.41013e1,4.83563e1}, + {2.65438e-1,-1.93341e0,1.40998e1,2.41876e1,-1.39034e2,-2.183e2}, + {-6.92691e-1,6.47135e-1,-1.58398e1,-2.90581e1,1.5396e2,2.56697e2}, + {1.26243e-1,-4.08783e-1,5.21125e0,1.011e1,-5.11262e1,-8.8713e1} + }}, + //Pd................................................. + CoeffMat{{ + {9.99888e-1,-1.30188e-4,3.78436e-3,2.22971e-3,-2.99185e-2,-3.96315e-2}, + {3.9875e-1,1.26855e0,-4.07427e0,-5.38796e0,3.5955e1,5.18546e1}, + {3.20748e-1,-1.97817e0,1.44153e1,2.6089e1,-1.43866e2,-2.29578e2}, + {-7.39891e-1,6.76679e-1,-1.59819e1,-3.09162e1,1.57312e2,2.66867e2}, + {1.3563e-1,-4.31972e-1,5.2031e0,1.06642e1,-5.17104e1,-9.14493e1} + }}, + //Ag................................................. + CoeffMat{{ + {9.99884e-1,-1.57459e-4,4.12052e-3,2.69984e-3,-3.28468e-2,-4.41512e-2}, + {3.93891e-1,1.3171e0,-4.20944e0,-5.93691e0,3.76535e1,5.52188e1}, + {3.79857e-1,-2.01791e0,1.46238e1,2.79653e1,-1.47893e2,-2.39997e2}, + {-7.89852e-1,7.01196e-1,-1.59929e1,-3.27077e1,1.59613e2,2.75813e2}, + {1.45646e-1,-4.54328e-1,5.14701e0,1.11863e1,-5.18977e1,-9.36987e1} + }}, + //Cd................................................. + CoeffMat{{ + {9.99881e-1,-1.86816e-4,4.45491e-3,3.19574e-3,-3.57812e-2,-4.87457e-2}, + {3.87612e-1,1.36524e0,-4.31686e0,-6.49005e0,3.91612e1,5.84032e1}, + {4.4294e-1,-2.05189e0,1.47103e1,2.98033e1,-1.50988e2,-2.49389e2}, + {-8.42683e-1,7.20045e-1,-1.58579e1,-3.44169e1,1.60733e2,2.83354e2}, + {1.56312e-1,-4.75705e-1,5.0381e0,1.16708e1,-5.16454e1,-9.53999e1} + }}, + //In................................................. + CoeffMat{{ + {9.99878e-1,-2.1848e-4,4.78376e-3,3.71897e-3,-3.86926e-2,-5.33866e-2}, + {3.79879e-1,1.41265e0,-4.39391e0,-7.04282e0,4.0455e1,6.13721e1}, + {5.10001e-1,-2.07896e0,1.46666e1,3.15843e1,-1.53072e2,-2.57624e2}, + {-8.98306e-1,7.32213e-1,-1.55686e1,-3.60229e1,1.60591e2,2.89348e2}, + {1.67591e-1,-4.95837e-1,4.87389e0,1.21106e1,-5.09274e1,-9.6506e1} + }}, + //Sn................................................ + CoeffMat{{ + {9.99876e-1,-2.52173e-4,5.09558e-3,4.26518e-3,-4.14939e-2,-5.79712e-2}, + {3.70689e-1,1.45908e0,-4.43983e0,-7.59192e0,4.15261e1,6.4108e1}, + {5.80924e-1,-2.09831e0,1.44911e1,3.32945e1,-1.54118e2,-2.64634e2}, + {-9.56518e-1,7.37009e-1,-1.51243e1,-3.75098e1,1.59159e2,2.93723e2}, + {1.79397e-1,-5.14573e-1,4.65426e0,1.25003e1,-4.97362e1,-9.69932e1} + }}, + //Sb................................................. + CoeffMat{{ + {9.99874e-1,-2.87917e-4,5.39066e-3,4.83313e-3,-4.41852e-2,-6.24953e-2}, + {3.60001e-1,1.5042e0,-4.4515e0,-8.1327e0,4.23476e1,6.65709e1}, + {6.55716e-1,-2.10882e0,1.41741e1,3.49157e1,-1.54034e2,-2.70276e2}, + {-1.01724e0,7.33501e-1,-1.45156e1,-3.88572e1,1.56348e2,2.96328e2}, + {1.9169e-1,-5.3169e-1,4.37639e0,1.28328e1,-4.80435e1,-9.68125e1} + }}, + //Te................................................. + CoeffMat{{ + {9.99874e-1,-3.25847e-4,5.67083e-3,5.42945e-3,-4.67877e-2,-6.701e-2}, + {3.47783e-1,1.54767e0,-4.42606e0,-8.65995e0,4.28934e1,6.87183e1}, + {7.34353e-1,-2.10941e0,1.3707e1,3.64273e1,-1.52736e2,-2.74401e2}, + {-1.08036e0,7.20771e-1,-1.37344e1,-4.00427e1,1.52073e2,2.97007e2}, + {2.04417e-1,-5.46972e-1,4.03784e0,1.31007e1,-4.58224e1,-9.59123e1} + }}, + //I................................................. + CoeffMat{{ + {9.99875e-1,-3.65406e-4,5.92115e-3,6.03332e-3,-4.91674e-2,-7.12951e-2}, + {3.34062e-1,1.58924e0,-4.36361e0,-9.17241e0,4.31638e1,7.05522e1}, + {8.16604e-1,-2.09933e0,1.30915e1,3.78235e1,-1.50231e2,-2.77015e2}, + {-1.14554e0,6.98298e-1,-1.2784e1,-4.10591e1,1.46348e2,2.9577e2}, + {2.17451e-1,-5.60344e-1,3.63994e0,1.33015e1,-4.30802e1,-9.42981e1} + }}, + //Xe.................................................. + CoeffMat{{ + {9.99877e-1,-4.06637e-4,6.14278e-3,6.64937e-3,-5.13391e-2,-7.53881e-2}, + {3.18822e-1,1.62854e0,-4.26169e0,-9.66459e0,4.31357e1,7.20331e1}, + {9.02373e-1,-2.07743e0,1.23209e1,3.90832e1,-1.46445e2,-2.77984e2}, + {-1.21259e0,6.65174e-1,-1.16583e1,-4.18839e1,1.39104e2,2.92479e2}, + {2.3071e-1,-5.71608e-1,3.18104e0,1.34277e1,-3.97963e1,-9.19255e1} + }}, + //Cs.................................................. + CoeffMat{{ + {9.99881e-1,-4.49175e-4,6.32631e-3,7.27184e-3,-5.32297e-2,-7.91972e-2}, + {3.02086e-1,1.66529e0,-4.11996e0,-1.0133e1,4.28031e1,7.31464e1}, + {9.91429e-1,-2.04292e0,1.13959e1,4.01931e1,-1.41369e2,-2.7726e2}, + {-1.28117e0,6.2087e-1,-1.03596e1,-4.25024e1,1.30338e2,2.8709e2}, + {2.44065e-1,-5.80702e-1,2.66217e0,1.34745e1,-3.59721e1,-8.87824e1} + }}, + //Ba.................................................. + CoeffMat{{ + {9.99886e-1,-4.93136e-4,6.47914e-3,7.90029e-3,-5.48909e-2,-8.27736e-2}, + {2.8384e-1,1.69905e0,-3.93534e0,-1.05724e1,4.21392e1,7.38521e1}, + {1.08366e0,-1.99451e0,1.03076e1,4.1134e1,-1.34917e2,-2.7471e2}, + {-1.35107e0,5.64397e-1,-8.87993e0,-4.28945e1,1.19972e2,2.7947e2}, + {2.57431e-1,-5.87417e-1,2.08111e0,1.34353e1,-3.15844e1,-8.48268e1} + }}, + //La.................................................. + CoeffMat{{ + {9.99892e-1,-5.38122e-4,6.59206e-3,8.52848e-3,-5.62508e-2,-8.60251e-2}, + {2.6413e-1,1.72952e0,-3.70812e0,-1.09794e1,4.1143e1,7.4141e1}, + {1.17876e0,-1.93141e0,9.05928e0,4.18928e1,-1.27099e2,-2.70309e2}, + {-1.42185e0,4.95292e-1,-7.22427e0,-4.30461e1,1.08024e2,2.69601e2}, + {2.70647e-1,-5.91727e-1,1.43982e0,1.33056e1,-2.66423e1,-8.00557e1} + }}, + //Ce.................................................. + CoeffMat{{ + {9.999e-1,-5.83896e-4,6.66373e-3,9.15389e-3,-5.72988e-2,-8.89368e-2}, + {2.42961e-1,1.75635e0,-3.43619e0,-1.13482e1,3.97928e1,7.39747e1}, + {1.27654e0,-1.85263e0,7.64589e0,4.24492e1,-1.17853e2,-2.63933e2}, + {-1.49322e0,4.12926e-1,-5.38909e0,-4.29363e1,9.44419e1,2.57363e2}, + {2.83597e-1,-5.93557e-1,7.3754e-1,1.30785e1,-2.11316e1,-7.44329e1} + }}, + //Pr.................................................. + CoeffMat{{ + {9.99909e-1,-6.301e-4,6.69295e-3,9.77077e-3,-5.8022e-2,-9.14799e-2}, + {2.20373e-1,1.77919e0,-3.11907e0,-1.16754e1,3.80818e1,7.33384e1}, + {1.37668e0,-1.75734e0,6.06819e0,4.27905e1,-1.07169e2,-2.55543e2}, + {-1.56475e0,3.16885e-1,-3.37687e0,-4.25518e1,7.92269e1,2.42726e2}, + {2.96124e-1,-5.92915e-1,-2.45072e-2,1.27501e1,-1.50562e1,-6.79514e1} + }}, + //Nd.................................................. + CoeffMat{{ + {9.9992e-1,-6.77211e-4,6.67917e-3,1.03866e-2,-5.84208e-2,-9.36775e-2}, + {1.96406e-1,1.79753e0,-2.7553e0,-1.19533e1,3.59929e1,7.21943e1}, + {1.47887e0,-1.64416e0,4.32381e0,4.28906e1,-9.50034e1,-2.45021e2}, + {-1.636e0,2.06169e-1,-1.18704e0,-4.18661e1,6.23476e1,2.25579e2}, + {3.08067e-1,-5.89622e-1,-8.4608e-1,1.23116e1,-8.40939e0,-6.05788e1} + }}, + //Pm.................................................. + CoeffMat{{ + {9.99932e-1,-7.23543e-4,6.61026e-3,1.09756e-2,-5.83892e-2,-9.53462e-2}, + {1.71135e-1,1.81127e0,-2.34665e0,-1.21818e1,3.35368e1,7.05547e1}, + {1.58269e0,-1.5131e0,2.4214e0,4.27487e1,-8.14091e1,-2.32422e2}, + {-1.70642e0,8.13302e-2,1.16945e0,-4.0879e1,4.38728e1,2.05995e2}, + {3.19226e-1,-5.84023e-1,-1.72313e0,1.17636e1,-1.21776e0,-5.23434e1} + }}, + //Sm.................................................. + CoeffMat{{ + {9.99946e-1,-7.69368e-4,6.48923e-3,1.15425e-2,-5.79523e-2,-9.65284e-2}, + {1.44613e-1,1.81998e0,-1.89196e0,-1.23548e1,3.06998e1,6.83894e1}, + {1.68776e0,-1.36316e0,3.59801e-1,4.23443e1,-6.63559e1,-2.17658e2}, + {-1.77552e0,-5.81387e-2,3.69184e0,-3.95701e1,2.37855e1,1.83897e2}, + {3.2942e-1,-5.76119e-1,-2.65496e0,1.10995e1,6.52027e0,-4.32241e1} + }}, + //Eu.................................................. + CoeffMat{{ + {9.99962e-1,-8.13654e-4,6.31217e-3,1.20763e-2,-5.70782e-2,-9.71683e-2}, + {1.16915e-1,1.82343e0,-1.39208e0,-1.24689e1,2.74833e1,6.56914e1}, + {1.79364e0,-1.194e0,-1.85513e0,4.16665e1,-4.9868e1,-2.00724e2}, + {-1.84272e0,-2.12012e-1,6.37189e0,-3.79293e1,2.12742e0,1.59295e2}, + {3.38449e-1,-5.66167e-1,-3.63838e0,1.03165e1,1.47866e1,-3.32296e1} + }}, + //Gd.................................................. + CoeffMat{{ + {9.9998e-1,-8.57331e-4,6.07736e-3,1.25819e-2,-5.57471e-2,-9.72475e-2}, + {8.8154e-2,1.82105e0,-8.47718e-1,-1.25186e1,2.38891e1,6.2451e1}, + {1.89976e0,-1.00428e0,-4.21799e0,4.0697e1,-3.19703e1,-1.81604e2}, + {-1.90731e0,-3.8108e-1,9.2019e0,-3.59386e1,-2.10594e1,1.32192e2}, + {3.46059e-1,-5.54093e-1,-4.67039e0,9.40924e0,2.35631e1,-2.23653e1} + }}, + //Tb.................................................. + CoeffMat{{ + {9.99999e-1,-8.99343e-4,5.7868e-3,1.30512e-2,-5.39764e-2,-9.67723e-2}, + {5.84024e-2,1.81266e0,-2.59193e-1,-1.25002e1,1.99139e1,5.86545e1}, + {2.00567e0,-7.93885e-1,-6.72461e0,3.94244e1,-1.26714e1,-1.60273e2}, + {-1.96873e0,-5.64782e-1,1.21753e1,-3.3588e1,-4.57487e1,1.02578e2}, + {3.5205e-1,-5.40282e-1,-5.74837e0,8.37487e0,3.28364e1,-1.06341e1} + }}, + //Dy.................................................. + CoeffMat{{ + {1.00002e0,-9.39311e-4,5.43607e-3,1.34763e-2,-5.17249e-2,-9.56765e-2}, + {2.78005e-2,1.79795e0,3.70737e-1,-1.24114e1,1.55759e1,5.43184e1}, + {2.1107e0,-5.62473e-1,-9.36239e0,3.78424e1,7.94516e0,-1.36806e2}, + {-2.02617e0,-7.62769e-1,1.52768e1,-3.08728e1,-7.18354e1,7.05554e1}, + {3.5614e-1,-5.25061e-1,-6.86679e0,7.21247e0,4.25672e1,1.92463e0} + }}, + //Ho................................................... + CoeffMat{{ + {1.00004e0,-9.76747e-4,5.02266e-3,1.3853e-2,-4.89737e-2,-9.3934e-2}, + {-3.54744e-3,1.77664e0,1.04132e0,-1.22473e1,1.08741e1,4.94269e1}, + {2.21429e0,-3.09743e-1,-1.21254e1,3.59359e1,2.986e1,-1.11177e2}, + {-2.07893e0,-9.74603e-1,1.84979e1,-2.77795e1,-9.928e1,3.61173e1}, + {3.5809e-1,-5.08796e-1,-8.02233e0,5.91833e0,5.27374e1,1.53069e1} + }}, + //Er................................................... + CoeffMat{{ + {1.00007e0,-1.01117e-3,4.54349e-3,1.4172e-2,-4.56907e-2,-9.14834e-2}, + {-3.54805e-2,1.74848e0,1.74921e0,-1.20063e1,5.83106e0,4.40029e1}, + {2.3157e0,-3.56472e-2,-1.49988e1,3.37013e1,5.29727e1,-8.34879e1}, + {-2.12613e0,-1.19953e0,2.18208e1,-2.43067e1,-1.27958e2,-6.05797e-1}, + {3.57593e-1,-4.91966e-1,-9.2087e0,4.49274e0,6.33009e1,2.94631e1} + }}, + //Tm................................................... + CoeffMat{{ + {1.00009e0,-1.04219e-3,3.99979e-3,1.44301e-2,-4.18849e-2,-8.83308e-2}, + {-6.78832e-2,1.71323e0,2.49363e0,-1.16834e1,4.45978e-1,3.80313e1}, + {2.41433e0,2.59886e-1,-1.79765e1,3.11249e1,7.72617e1,-5.37152e1}, + {-2.16704e0,-1.4368e0,2.52369e1,-2.04429e1,-1.57828e2,-3.9614e1}, + {3.544e-1,-4.75067e-1,-1.04225e1,2.93269e0,7.42385e1,4.43866e1} + }}, + //Yb................................................... + CoeffMat{{ + {1.00012e0,-1.06927e-3,3.39091e-3,1.46208e-2,-3.75471e-2,-8.44506e-2}, + {-1.00611e-1,1.67068e0,3.27246e0,-1.12756e1,-5.27058e0,3.15152e1}, + {2.50948e0,5.76648e-1,-2.10477e1,2.81988e1,1.02665e2,-2.18989e1}, + {-2.20084e0,-1.68528e0,2.87325e1,-1.61828e1,-1.88802e2,-8.08394e1}, + {3.48226e-1,-4.58722e-1,-1.16589e1,1.23735e0,8.55166e1,6.00477e1} + }}, + //Lu................................................... + CoeffMat{{ + {1.00015e0,-1.09216e-3,2.71745e-3,1.47404e-2,-3.26779e-2,-7.98343e-2}, + {-1.33495e-1,1.62059e0,4.08307e0,-1.07793e1,-1.13042e1,2.44607e1}, + {2.60038e0,9.14519e-1,-2.42e1,2.49142e1,1.29108e2,1.19101e1}, + {-2.22661e0,-1.94391e0,3.22924e1,-1.15206e1,-2.20784e2,-1.24202e2}, + {3.38764e-1,-4.43546e-1,-1.29122e1,-5.9428e-1,9.70972e1,7.64128e1} + }}, + //Hf.................................................. + CoeffMat{{ + {1.00018e0,-1.11005e-3,1.98019e-3,1.47814e-2,-2.72814e-2,-7.44728e-2}, + {-1.66392e-1,1.56288e0,4.92359e0,-1.01915e1,-1.76472e1,1.68656e1}, + {2.68631e0,1.27279e0,-2.74231e1,2.12642e1,1.56537e2,4.76851e1}, + {-2.24352e0,-2.21096e0,3.59035e1,-6.45262e0,-2.53694e2,-1.69646e2}, + {3.25734e-1,-4.30391e-1,-1.41779e1,-2.56236e0,1.08949e2,9.34555e1} + }}, + //Ta.................................................. + CoeffMat{{ + {1.00021e0,-1.1227e-3,1.1875e-3,1.47467e-2,-2.14258e-2,-6.84575e-2}, + {-1.99145e-1,1.49739e0,5.79226e0,-9.50812e0,-2.42938e1,8.72312e0}, + {2.76656e0,1.65088e0,-3.07077e1,1.72391e1,1.84905e2,8.54178e1}, + {-2.25073e0,-2.48478e0,3.95541e1,-9.72452e-1,-2.87462e2,-2.17133e2}, + {3.0885e-1,-4.20088e-1,-1.54515e1,-4.66797e0,1.21043e2,1.11156e2} + }}, + //W................................................... + CoeffMat{{ + {1.00025e0,-1.12974e-3,3.25083e-4,1.46196e-2,-1.49818e-2,-6.15916e-2}, + {-2.31499e-1,1.42403e0,6.68301e0,-8.72894e0,-3.12005e1,8.13039e-2}, + {2.84004e0,2.04799e0,-3.40292e1,1.28426e1,2.14035e2,1.24907e2}, + {-2.24701e0,-2.76343e0,4.3216e1,4.91177e0,-3.2188e2,-2.66423e2}, + {2.87708e-1,-4.13579e-1,-1.67235e1,-6.90724e0,1.33305e2,1.29426e2} + }}, + //Re.................................................. + CoeffMat{{ + {1.00028e0,-1.13018e-3,-5.85208e-4,1.44008e-2,-8.12721e-3,-5.40793e-2}, + {-2.63328e-1,1.34285e0,7.59541e0,-7.85118e0,-3.83723e1,-9.07534e0}, + {2.90612e0,2.46267e0,-3.73829e1,8.06978e0,2.43918e2,1.66172e2}, + {-2.23164e0,-3.04426e0,4.68824e1,1.12007e1,-3.56918e2,-3.17506e2}, + {2.62068e-1,-4.12056e-1,-1.79911e1,-9.27922e0,1.45719e2,1.48255e2} + }}, + //Os.................................................. + CoeffMat{{ + {1.00032e0,-1.12442e-3,-1.55891e-3,1.40881e-2,-7.3226e-4,-4.57754e-2}, + {-2.94323e-1,1.25374e0,8.52167e0,-6.87354e0,-4.57537e1,-1.8691e1}, + {2.96357e0,2.89403e0,-4.07385e1,2.92128e0,2.74335e2,2.08986e2}, + {-2.20324e0,-3.32515e0,5.05197e1,1.78893e1,-3.92323e2,-3.70113e2}, + {2.31474e-1,-4.16536e-1,-1.92429e1,-1.1781e1,1.58197e2,1.67545e2} + }}, + //Ir................................................... + CoeffMat{{ + {1.00036e0,-1.11107e-3,-2.58076e-3,1.36692e-2,7.08911e-3,-3.6773e-2}, + {-3.24316e-1,1.15695e0,9.45947e0,-5.79675e0,-5.33328e1,-2.87528e1}, + {3.01159e0,3.33981e0,-4.4085e1,-2.59468e0,3.05217e2,2.53266e2}, + {-2.16093e0,-3.60244e0,5.41141e1,2.49638e1,-4.28002e2,-4.24126e2}, + {1.95645e-1,-4.28567e-1,-2.04742e1,-1.44066e1,1.70702e2,1.87249e2} + }}, + //Pt................................................... + CoeffMat{{ + {1.00039e0,-1.09011e-3,-3.65347e-3,1.31413e-2,1.53649e-2,-2.70335e-2}, + {-3.53028e-1,1.05257e0,1.04028e1,-4.62034e0,-6.10681e1,-3.92186e1}, + {3.04907e0,3.79831e0,-4.73979e1,-8.47391e0,3.36394e2,2.98829e2}, + {-2.10347e0,-3.87306e0,5.76385e1,3.24147e1,-4.63751e2,-4.79318e2}, + {1.54174e-1,-4.49506e-1,-2.16756e1,-1.71516e1,1.83161e2,2.07283e2} + }}, + //Au................................................... + CoeffMat{{ + {1.00043e0,-1.06114e-3,-4.7734e-3,1.25001e-2,2.40713e-2,-1.65728e-2}, + {-3.80207e-1,9.40778e-1,1.13464e1,-3.34423e0,-6.89259e1,-5.00549e1}, + {3.07493e0,4.26732e0,-5.06567e1,-1.47102e1,3.67721e2,3.45521e2}, + {-2.0297e0,-4.13334e0,6.10696e1,4.02298e1,-4.99399e2,-5.35495e2}, + {1.06694e-1,-4.80932e-1,-2.28393e1,-2.00106e1,1.95512e2,2.27574e2} + }}, + //Hg................................................. + CoeffMat{{ + {1.00047e0,-1.02377e-3,-5.93877e-3,1.17393e-2,3.32021e-2,-5.38025e-3}, + {-4.05577e-1,8.21834e-1,1.22846e1,-1.96909e0,-7.68675e1,-6.12207e1}, + {3.08808e0,4.74434e0,-5.38383e1,-2.12945e1,3.99035e2,3.93158e2}, + {-1.93842e0,-4.37923e0,6.43816e1,4.83938e1,-5.3475e2,-5.92428e2}, + {5.28167e-2,-5.24544e-1,-2.39565e1,-2.29768e1,2.07685e2,2.48037e2} + }}, + //Tl................................................. + CoeffMat{{ + {1.00051e0,-9.77691e-4,-7.14416e-3,1.08563e-2,4.27196e-2,6.50699e-3}, + {-4.28882e-1,6.96049e-1,1.32126e1,-4.94649e-1,-8.48638e1,-7.26907e1}, + {3.08745e0,5.22659e0,-5.69235e1,-2.82201e1,4.30208e2,4.41611e2}, + {-1.82849e0,-4.60634e0,6.7553e1,5.68935e1,-5.69648e2,-6.49946e2}, + {-7.81506e-3,-5.82177e-1,-2.50201e1,-2.60444e1,2.19621e2,2.68607e2} + }}, + //Pb................................................. + CoeffMat{{ + {1.00055e0,-9.22411e-4,-8.38967e-3,9.83942e-3,5.26394e-2,1.91419e-2}, + {-4.49833e-1,5.63865e-1,1.41246e1,1.0768e0,-9.28737e1,-8.44177e1}, + {3.07189e0,5.71077e0,-5.98888e1,-3.54715e1,4.61066e2,4.9067e2}, + {-1.69869e0,-4.80968e0,7.05579e1,6.57062e1,-6.03887e2,-7.07793e2}, + {-7.55851e-2,-6.55871e-1,-2.60213e1,-2.92042e1,2.31249e2,2.89188e2} + }}, + //Bi................................................. + CoeffMat{{ + {1.00059e0,-8.58152e-4,-9.6602e-3,8.70229e-3,6.28328e-2,3.23376e-2}, + {-4.68192e-1,4.25668e-1,1.50174e1,2.74763e0,-1.00883e2,-9.64012e1}, + {3.04043e0,6.19366e0,-6.27207e1,-4.30487e1,4.91532e2,5.40291e2}, + {-1.54799e0,-4.98431e0,7.3381e1,7.48252e1,-6.37365e2,-7.65885e2}, + {-1.5081e-1,-7.47659e-1,-2.69551e1,-3.24524e1,2.42528e2,3.09744e2} + }}, + //Po................................................. + CoeffMat{{ + {1.00062e0,-7.84582e-4,-1.09553e-2,7.43533e-3,7.33106e-2,4.61358e-2}, + {-4.83646e-1,2.8205e-1,1.58842e1,4.51493e0,-1.08846e2,-1.08589e2}, + {2.99185e0,6.67128e0,-6.53938e1,-5.09329e1,5.21419e2,5.90246e2}, + {-1.37512e0,-5.12439e0,7.59949e1,8.42238e1,-6.69862e2,-8.23945e2}, + {-2.33878e-1,-8.59879e-1,-2.78125e1,-3.57783e1,2.53381e2,3.30173e2} + }}, + //At................................................. + CoeffMat{{ + {1.00066e0,-7.01484e-4,-1.22684e-2,6.03454e-3,8.40298e-2,6.05018e-2}, + {-4.95899e-1,1.3369e-1,1.67195e1,6.37574e0,-1.16723e2,-1.20936e2}, + {2.92499e0,7.13921e0,-6.78861e1,-5.91051e1,5.50562e2,6.40337e2}, + {-1.17889e0,-5.22361e0,7.8376e1,9.38748e1,-7.01185e2,-8.81728e2}, + {-3.25146e-1,-9.95032e-1,-2.85858e1,-3.91713e1,2.63738e2,3.50385e2} + }}, + //Rn................................................. + CoeffMat{{ + {1.0007e0,-6.08729e-4,-1.35873e-2,4.50061e-3,9.48991e-2,7.53277e-2}, + {-5.04647e-1,-1.86445e-2,1.75175e1,8.32729e0,-1.24477e2,-1.33405e2}, + {2.83871e0,7.59269e0,-7.01766e1,-6.75463e1,5.78809e2,6.90388e2}, + {-9.58143e-1,-5.27523e0,8.0502e1,1.03751e2,-7.31155e2,-9.39013e2}, + {-4.24964e-1,-1.15578e0,-2.92679e1,-4.26205e1,2.73537e2,3.70298e2} + }}, + //Fr................................................. + CoeffMat{{ + {1.00073e0,-5.06594e-4,-1.49195e-2,2.82752e-3,1.05984e-1,9.07179e-2}, + {-5.09498e-1,-1.74114e-1,1.82691e1,1.0364e1,-1.32042e2,-1.45912e2}, + {2.73154e0,8.02658e0,-7.22315e1,-7.62278e1,6.05902e2,7.40063e2}, + {-7.11399e-1,-5.27209e0,8.23375e1,1.13815e2,-7.59479e2,-9.95409e2}, + {-5.33773e-1,-1.34491e0,-2.98474e1,-4.61115e1,2.82675e2,3.89772e2} + }}, + //Ra................................................. + CoeffMat{{ + {1.00076e0,-3.94938e-4,-1.62461e-2,1.01616e-3,1.17156e-1,1.06529e-1}, + {-5.10179e-1,-3.3169e-1,1.89693e1,1.24811e1,-1.39382e2,-1.58412e2}, + {2.60247e0,8.43503e0,-7.4033e1,-8.51228e1,6.31692e2,7.89166e2}, + {-4.37673e-1,-5.2062e0,8.38642e1,1.2403e2,-7.85984e2,-1.05067e3}, + {-6.5185e-1,-1.56551e0,-3.03186e1,-4.96307e1,2.91091e2,4.08719e2} + }}, + //Ac................................................. + CoeffMat{{ + {1.00079e0,-2.74014e-4,-1.75614e-2,-9.33046e-4,1.28369e-1,1.22714e-1}, + {-5.06317e-1,-4.90282e-1,1.96102e1,1.46725e1,-1.4644e2,-1.70838e2}, + {2.45014e0,8.81187e0,-7.5553e1,-9.42005e1,6.55963e2,8.37419e2}, + {-1.35635e-1,-5.06921e0,8.50532e1,1.34356e2,-8.10425e2,-1.10448e3}, + {-7.79579e-1,-1.82077e0,-3.06725e1,-5.31628e1,2.987e2,4.2702e2} + }}, + //Th................................................ + CoeffMat{{ + {1.00082e0,-1.43895e-4,-1.8859e-2,-3.02404e-3,1.39581e-1,1.39236e-1}, + {-4.97537e-1,-6.48585e-1,2.01838e1,1.69296e1,-1.53157e2,-1.83108e2}, + {2.27321e0,9.15011e0,-7.6763e1,-1.0342e2,6.78484e2,8.84496e2}, + {1.9599e-1,-4.85189e0,8.5876e1,1.44742e2,-8.32543e2,-1.15645e3}, + {-9.17319e-1,-2.1142e0,-3.09003e1,-5.66889e1,3.05413e2,4.44542e2} + }}, + //Pa................................................. + CoeffMat{{ + {1.00085e0,-5.30411e-6,-2.01239e-2,-5.2478e-3,1.50683e-1,1.55963e-1}, + {-4.83526e-1,-8.05262e-1,2.06846e1,1.92461e1,-1.59496e2,-1.95177e2}, + {2.07059e0,9.44256e0,-7.76442e1,-1.12749e2,6.99103e2,9.30193e2}, + {5.58224e-1,-4.54471e0,8.63141e1,1.55146e2,-8.52163e2,-1.20633e3}, + {-1.06534e0,-2.44938e0,-3.09967e1,-6.01932e1,3.11168e2,4.61192e2} + }}, + //U.................................................... + CoeffMat{{ + {1.00087e0,1.42049e-4,-2.13455e-2,-7.61031e-3,1.61591e-1,1.72814e-1}, + {-4.6392e-1,-9.58725e-1,2.11056e1,2.16134e1,-1.65402e2,-2.06974e2}, + {1.841e0,9.68106e0,-7.81727e1,-1.22145e2,7.17614e2,9.74218e2}, + {9.52222e-1,-4.13715e0,8.63446e1,1.65515e2,-8.69054e2,-1.25378e3}, + {-1.22395e0,-2.83024e0,-3.09548e1,-6.36564e1,3.15887e2,4.76849e2} + }}}; + // clang-format on + static_assert(std::size(mott_coeffs) == MottElementData::num_mott_elements, + "wrong number of Mott coefficient elements"); + + int index = z.unchecked_get() - 1; + CELER_VALIDATE( + index >= 0 && index < int{MottElementData::num_mott_elements}, + << "atomic number " << z.get() + << " is out of range for Coulomb scattering model Mott coefficients " + "(must be less than " + << MottElementData::num_mott_elements << ")"); + + return mott_coeffs[index]; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/em/params/WentzelOKVIParams.hh b/src/celeritas/em/params/WentzelOKVIParams.hh new file mode 100644 index 0000000000..14b93495de --- /dev/null +++ b/src/celeritas/em/params/WentzelOKVIParams.hh @@ -0,0 +1,85 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. +// See the top-level COPYRIGHT file for details. +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/em/params/WentzelOKVIParams.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/data/CollectionMirror.hh" +#include "corecel/data/ParamsDataInterface.hh" +#include "celeritas/em/data/WentzelOKVIData.hh" +#include "celeritas/mat/IsotopeView.hh" +#include "celeritas/phys/AtomicNumber.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +class MaterialParams; +struct ImportData; + +//---------------------------------------------------------------------------// +/*! + * Construct and store shared Coulomb and multiple scattering data. + * + * This data is used by both the single Coulomb scattering and Wentzel VI + * multiple scattering models. + */ +class WentzelOKVIParams final : public ParamsDataInterface +{ + public: + //!@{ + //! \name Type aliases + using SPConstMaterials = std::shared_ptr; + //!@} + + public: + struct Options + { + //! Use combined single and multiple scattering + bool is_combined{true}; + //! Polar angle limit between single and multiple scattering + real_type polar_angle_limit{constants::pi}; + //! Factor for dynamic computation of angular limit between SS and MSC + real_type angle_limit_factor{1}; + //! User defined screening factor + real_type screening_factor{1}; + //! Nuclear form factor model + NuclearFormFactorType form_factor{NuclearFormFactorType::exponential}; + }; + + public: + // Construct if Wentzel VI or Coulomb is present, else return nullptr + static std::shared_ptr + from_import(ImportData const& data, SPConstMaterials materials); + + // Construct from material data and options + WentzelOKVIParams(SPConstMaterials materials, Options options); + + //! Access Wentzel OK&VI data on the host + HostRef const& host_ref() const final { return data_.host_ref(); } + + //! Access Wentzel OK&VI data on the device + DeviceRef const& device_ref() const final { return data_.device_ref(); } + + private: + // Host/device storage and reference + CollectionMirror data_; + + // Construct per-element data (loads Mott coefficients) + void build_data(HostVal& host_data, + MaterialParams const& materials); + + // Retrieve matrix of interpolated Mott coefficients + static MottElementData::MottCoeffMatrix + get_mott_coeff_matrix(AtomicNumber z); + + // Calculate the nuclear form prefactor + static real_type calc_nuclear_form_prefactor(IsotopeView const& iso); +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/em/params/WentzelVIMscParams.cc b/src/celeritas/em/params/WentzelVIMscParams.cc index 6f7048a994..090aae4189 100644 --- a/src/celeritas/em/params/WentzelVIMscParams.cc +++ b/src/celeritas/em/params/WentzelVIMscParams.cc @@ -29,10 +29,7 @@ WentzelVIMscParams::from_import(ParticleParams const& particles, MaterialParams const& materials, ImportData const& data) { - auto is_wentzel = [](ImportMscModel const& imm) { - return imm.model_class == ImportModelClass::wentzel_vi_uni; - }; - if (!std::any_of(data.msc_models.begin(), data.msc_models.end(), is_wentzel)) + if (!has_msc_model(data, ImportModelClass::wentzel_vi_uni)) { // No WentzelVI MSC present return nullptr; diff --git a/src/celeritas/em/params/detail/MscParamsHelper.cc b/src/celeritas/em/params/detail/MscParamsHelper.cc index 5b80c43988..52ea078907 100644 --- a/src/celeritas/em/params/detail/MscParamsHelper.cc +++ b/src/celeritas/em/params/detail/MscParamsHelper.cc @@ -43,7 +43,7 @@ MscParamsHelper::MscParamsHelper(ParticleParams const& particles, /*! * Validate and save MSC IDs. */ -void MscParamsHelper::build_ids(MscIds* ids) const +void MscParamsHelper::build_ids(CoulombIds* ids) const { ids->electron = particles_.find(pdg::electron()); ids->positron = particles_.find(pdg::positron()); diff --git a/src/celeritas/em/params/detail/MscParamsHelper.hh b/src/celeritas/em/params/detail/MscParamsHelper.hh index 978c1fe8bd..0059894f33 100644 --- a/src/celeritas/em/params/detail/MscParamsHelper.hh +++ b/src/celeritas/em/params/detail/MscParamsHelper.hh @@ -11,7 +11,7 @@ #include "corecel/Types.hh" #include "corecel/data/Collection.hh" -#include "celeritas/em/data/MscData.hh" +#include "celeritas/em/data/CommonCoulombData.hh" #include "celeritas/io/ImportModel.hh" namespace celeritas @@ -42,7 +42,7 @@ class MscParamsHelper VecImportMscModel const&, ImportModelClass); - void build_ids(MscIds* ids) const; + void build_ids(CoulombIds* ids) const; void build_xs(XsValues*, Values*) const; private: diff --git a/src/celeritas/em/process/CoulombScatteringProcess.cc b/src/celeritas/em/process/CoulombScatteringProcess.cc index 5eac088a7e..aafc0f2ee3 100644 --- a/src/celeritas/em/process/CoulombScatteringProcess.cc +++ b/src/celeritas/em/process/CoulombScatteringProcess.cc @@ -19,13 +19,10 @@ namespace celeritas /*! * Construct from host data. */ -CoulombScatteringProcess::CoulombScatteringProcess( - SPConstParticles particles, - SPConstMaterials materials, - SPConstImported process_data, - CoulombScatteringModel::Options const& options) +CoulombScatteringProcess::CoulombScatteringProcess(SPConstParticles particles, + SPConstImported process_data, + Options const& options) : particles_(std::move(particles)) - , materials_(std::move(materials)) , imported_(process_data, particles_, ImportProcessClass::coulomb_scat, @@ -33,8 +30,6 @@ CoulombScatteringProcess::CoulombScatteringProcess( , options_(options) { CELER_EXPECT(particles_); - CELER_EXPECT(materials_); - CELER_EXPECT(options_); } //---------------------------------------------------------------------------// @@ -45,7 +40,7 @@ auto CoulombScatteringProcess::build_models(ActionIdIter start_id) const -> VecModel { return {std::make_shared( - *start_id++, *particles_, *materials_, options_, imported_.processes())}; + *start_id++, *particles_, imported_.processes())}; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/em/process/CoulombScatteringProcess.hh b/src/celeritas/em/process/CoulombScatteringProcess.hh index a2f8db943d..92038b2ab4 100644 --- a/src/celeritas/em/process/CoulombScatteringProcess.hh +++ b/src/celeritas/em/process/CoulombScatteringProcess.hh @@ -12,7 +12,6 @@ #include "celeritas/em/data/CoulombScatteringData.hh" #include "celeritas/em/model/CoulombScatteringModel.hh" #include "celeritas/io/ImportParameters.hh" -#include "celeritas/mat/MaterialParams.hh" #include "celeritas/phys/Applicability.hh" #include "celeritas/phys/ImportedProcessAdapter.hh" #include "celeritas/phys/Process.hh" @@ -30,16 +29,20 @@ class CoulombScatteringProcess : public Process //!@{ //! \name Type aliases using SPConstParticles = std::shared_ptr; - using SPConstMaterials = std::shared_ptr; using SPConstImported = std::shared_ptr; //!@} + struct Options + { + //! Whether to use integral method to sample interaction length + bool use_integral_xs{true}; + }; + public: //! Construct from Coulomb scattering data CoulombScatteringProcess(SPConstParticles particles, - SPConstMaterials materials, SPConstImported process_data, - CoulombScatteringModel::Options const& options); + Options const& options); //! Construct the models associated with this process VecModel build_models(ActionIdIter start_id) const final; @@ -55,9 +58,8 @@ class CoulombScatteringProcess : public Process private: SPConstParticles particles_; - SPConstMaterials materials_; ImportedProcessAdapter imported_; - CoulombScatteringModel::Options options_; + Options options_; }; //---------------------------------------------------------------------------// diff --git a/src/celeritas/em/xs/MottRatioCalculator.hh b/src/celeritas/em/xs/MottRatioCalculator.hh index 28d626a639..68f9575aae 100644 --- a/src/celeritas/em/xs/MottRatioCalculator.hh +++ b/src/celeritas/em/xs/MottRatioCalculator.hh @@ -10,7 +10,7 @@ #include "corecel/Macros.hh" #include "corecel/Types.hh" #include "corecel/math/ArrayUtils.hh" -#include "celeritas/em/data/CoulombScatteringData.hh" +#include "celeritas/em/data/WentzelOKVIData.hh" #include "celeritas/grid/PolyEvaluator.hh" namespace celeritas @@ -31,14 +31,13 @@ class MottRatioCalculator public: //! Construct with state data inline CELER_FUNCTION - MottRatioCalculator(CoulombScatteringElementData const& element_data, - real_type beta); + MottRatioCalculator(MottElementData const& element_data, real_type beta); //! Ratio of Mott and Rutherford cross sections inline CELER_FUNCTION real_type operator()(real_type cos_t) const; private: - CoulombScatteringElementData const& element_data_; + MottElementData const& element_data_; real_type beta_; }; @@ -49,8 +48,8 @@ class MottRatioCalculator * Construct with state data. */ CELER_FUNCTION -MottRatioCalculator::MottRatioCalculator( - CoulombScatteringElementData const& element_data, real_type beta) +MottRatioCalculator::MottRatioCalculator(MottElementData const& element_data, + real_type beta) : element_data_(element_data), beta_(beta) { CELER_EXPECT(0 <= beta_ && beta_ < 1); @@ -80,12 +79,14 @@ real_type MottRatioCalculator::operator()(real_type cos_theta) const real_type beta0 = beta_ - beta_shift; // Evaluate polynomial of powers of beta0 and fcos_t - CoulombScatteringElementData::ThetaArray theta_coeffs; + MottElementData::ThetaArray theta_coeffs; for (auto i : range(theta_coeffs.size())) { theta_coeffs[i] = PolyEvaluator(element_data_.mott_coeff[i])(beta0); } - return PolyEvaluator(theta_coeffs)(fcos_t); + real_type result = PolyEvaluator(theta_coeffs)(fcos_t); + CELER_ENSURE(result >= 0); + return result; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/em/xs/WentzelHelper.hh b/src/celeritas/em/xs/WentzelHelper.hh index be9d92155c..e9a6679f0a 100644 --- a/src/celeritas/em/xs/WentzelHelper.hh +++ b/src/celeritas/em/xs/WentzelHelper.hh @@ -10,6 +10,9 @@ #include "corecel/Macros.hh" #include "corecel/Types.hh" #include "corecel/math/Algorithms.hh" +#include "celeritas/em/data/CommonCoulombData.hh" +#include "celeritas/em/data/WentzelOKVIData.hh" +#include "celeritas/mat/MaterialView.hh" #include "celeritas/phys/AtomicNumber.hh" #include "celeritas/phys/ParticleTrackView.hh" @@ -31,17 +34,21 @@ class WentzelHelper public: //!@{ //! \name Type aliases - using MomentumSq = units::MevMomentumSq; + using Charge = units::ElementaryCharge; using Energy = units::MevEnergy; using Mass = units::MevMass; + using MomentumSq = units::MevMomentumSq; //!@} public: // Construct from particle and material properties - inline CELER_FUNCTION WentzelHelper(ParticleTrackView const& particle, - AtomicNumber target_z, - CoulombScatteringRef const& data, - Energy cutoff); + inline CELER_FUNCTION + WentzelHelper(ParticleTrackView const& particle, + MaterialView const& material, + AtomicNumber target_z, + NativeCRef const& wentzel, + CoulombIds const& ids, + Energy cutoff); //! Get the target atomic number CELER_FUNCTION AtomicNumber atomic_number() const { return target_z_; } @@ -52,26 +59,45 @@ class WentzelHelper return screening_coefficient_; } + //! Get the Mott factor + CELER_FUNCTION real_type mott_factor() const { return mott_factor_; } + + //! Get the multiplicative factor for the cross section + CELER_FUNCTION real_type kin_factor() const { return kin_factor_; } + //! Get the maximum scattering angle off of electrons - CELER_FUNCTION real_type costheta_max_electron() const + CELER_FUNCTION real_type cos_thetamax_electron() const + { + return cos_thetamax_elec_; + } + + //! Get the maximum scattering angle off of a nucleus + CELER_FUNCTION real_type cos_thetamax_nuclear() const { - return cos_t_max_elec_; + return cos_thetamax_nuc_; } // The ratio of electron to total cross section for Coulomb scattering - inline CELER_FUNCTION real_type calc_xs_ratio() const; + inline CELER_FUNCTION real_type calc_xs_ratio(real_type cos_thetamin, + real_type cos_thetamax) const; + + // Calculate the electron cross section for Coulomb scattering + inline CELER_FUNCTION real_type + calc_xs_electron(real_type cos_thetamin, real_type cos_thetamax) const; + + // Calculate the nuclear cross section for Coulomb scattering + inline CELER_FUNCTION real_type + calc_xs_nuclear(real_type cos_thetamin, real_type cos_thetamax) const; private: //// DATA //// - // Target atomic number AtomicNumber const target_z_; - - // Moliere screening coefficient real_type screening_coefficient_; - - // Cosine of the maximum scattering angle off of electrons - real_type cos_t_max_elec_; + real_type kin_factor_; + real_type mott_factor_; + real_type cos_thetamax_elec_; + real_type cos_thetamax_nuc_; //// HELPER FUNCTIONS //// @@ -82,9 +108,23 @@ class WentzelHelper // Calculate the screening coefficient R^2 for electrons CELER_CONSTEXPR_FUNCTION real_type screen_r_sq_elec() const; + // Calculate the multiplicative factor for the cross section + inline CELER_FUNCTION real_type + calc_kin_factor(ParticleTrackView const&) const; + // Calculate the (cosine of) the maximum scattering angle off of electrons - inline CELER_FUNCTION real_type calc_costheta_max_electron( - ParticleTrackView const&, CoulombScatteringRef const&, Energy) const; + inline CELER_FUNCTION real_type calc_cos_thetamax_electron( + ParticleTrackView const&, CoulombIds const&, Energy) const; + + // Calculate the (cosine of) the maximum scattering angle off of a nucleus + inline CELER_FUNCTION real_type calc_cos_thetamax_nuclear( + ParticleTrackView const&, + MaterialView const& material, + NativeCRef const& wentzel) const; + + // Calculate the common factor in the electron and nuclear cross section + inline CELER_FUNCTION real_type calc_xs_factor(real_type cos_thetamin, + real_type cos_thetamax) const; }; //---------------------------------------------------------------------------// @@ -95,31 +135,76 @@ class WentzelHelper */ CELER_FUNCTION WentzelHelper::WentzelHelper(ParticleTrackView const& particle, + MaterialView const& material, AtomicNumber target_z, - CoulombScatteringRef const& data, + NativeCRef const& wentzel, + CoulombIds const& ids, Energy cutoff) : target_z_(target_z) , screening_coefficient_(this->calc_screening_coefficient(particle) - * data.screening_factor) - , cos_t_max_elec_(this->calc_costheta_max_electron(particle, data, cutoff)) + * wentzel.params.screening_factor) + , kin_factor_(this->calc_kin_factor(particle)) + , mott_factor_(particle.particle_id() == ids.electron + ? 1 + real_type(2e-4) * ipow<2>(target_z_.get()) + : 1) + , cos_thetamax_elec_( + this->calc_cos_thetamax_electron(particle, ids, cutoff)) + , cos_thetamax_nuc_( + this->calc_cos_thetamax_nuclear(particle, material, wentzel)) { CELER_EXPECT(screening_coefficient_ > 0); - CELER_EXPECT(cos_t_max_elec_ >= -1 && cos_t_max_elec_ <= 1); + CELER_EXPECT(cos_thetamax_elec_ >= -1 && cos_thetamax_elec_ <= 1); + CELER_EXPECT(cos_thetamax_nuc_ >= -1 && cos_thetamax_nuc_ <= 1); } //---------------------------------------------------------------------------// /*! * Ratio of electron cross section to total (nuclear + electron) cross section. */ -CELER_FUNCTION real_type WentzelHelper::calc_xs_ratio() const +CELER_FUNCTION real_type WentzelHelper::calc_xs_ratio( + real_type cos_thetamin, real_type cos_thetamax) const +{ + real_type xs_elec = this->calc_xs_electron(cos_thetamin, cos_thetamax); + return xs_elec + / (xs_elec + this->calc_xs_nuclear(cos_thetamin, cos_thetamax)); +} + +//---------------------------------------------------------------------------// +/*! + * Calculate the electron cross section for Coulomb scattering. + */ +CELER_FUNCTION real_type WentzelHelper::calc_xs_electron( + real_type cos_thetamin, real_type cos_thetamax) const +{ + cos_thetamin = max(cos_thetamin, cos_thetamax_elec_); + cos_thetamax = max(cos_thetamax, cos_thetamax_elec_); + if (cos_thetamin <= cos_thetamax) + { + return 0; + } + return this->calc_xs_factor(cos_thetamin, cos_thetamax); +} + +//---------------------------------------------------------------------------// +/*! + * Calculate the nuclear cross section for Coulomb scattering. + */ +CELER_FUNCTION real_type WentzelHelper::calc_xs_nuclear( + real_type cos_thetamin, real_type cos_thetamax) const { - // Calculating only reduced cross sections by elimination mutual factors - // in the ratio. - real_type nuc_xsec = target_z_.get() / (1 + screening_coefficient_); - real_type elec_xsec = (1 - cos_t_max_elec_) - / (1 - cos_t_max_elec_ + 2 * screening_coefficient_); + return target_z_.get() * this->calc_xs_factor(cos_thetamin, cos_thetamax); +} - return elec_xsec / (nuc_xsec + elec_xsec); +//---------------------------------------------------------------------------// +/*! + * Calculate the common factor in the electron and nuclear cross section. + */ +CELER_FUNCTION real_type WentzelHelper::calc_xs_factor( + real_type cos_thetamin, real_type cos_thetamax) const +{ + return kin_factor_ * mott_factor_ * (cos_thetamin - cos_thetamax) + / ((1 - cos_thetamin + 2 * screening_coefficient_) + * (1 - cos_thetamax + 2 * screening_coefficient_)); } //---------------------------------------------------------------------------// @@ -178,6 +263,32 @@ CELER_CONSTEXPR_FUNCTION real_type WentzelHelper::screen_r_sq_elec() const .value(); } +//---------------------------------------------------------------------------// +/*! + * Calculate the multiplicative factor for the cross section. + * + * This calculates the factor + * \f[ + f = \frac{2 \pi m_e^2 r_e^2 Z q^2}{\beta^2 p^2}, + * \f] + * where \f$ m_e, r_e, Z, q, \beta \f$, and \f$ p \f$ are the electron mass, + * classical electron radius, atomic number of the target atom, charge, + * relativistic speed, and momentum of the incident particle, respectively. + */ +CELER_FUNCTION real_type +WentzelHelper::calc_kin_factor(ParticleTrackView const& particle) const +{ + real_type constexpr twopi_mrsq + = 2 * constants::pi + * ipow<2>(native_value_to(constants::electron_mass).value() + * constants::r_electron); + + return twopi_mrsq * target_z_.get() + * ipow<2>(value_as(particle.charge())) + / (particle.beta_sq() + * value_as(particle.momentum_sq())); +} + //---------------------------------------------------------------------------// /*! * Calculate the maximum scattering angle off the target's electrons. @@ -185,15 +296,13 @@ CELER_CONSTEXPR_FUNCTION real_type WentzelHelper::screen_r_sq_elec() const * This calculates the cosine of the maximum polar angle that the incident * particle can scatter off of the target's electrons. */ -CELER_FUNCTION real_type -WentzelHelper::calc_costheta_max_electron(ParticleTrackView const& particle, - CoulombScatteringRef const& data, - Energy cutoff) const +CELER_FUNCTION real_type WentzelHelper::calc_cos_thetamax_electron( + ParticleTrackView const& particle, CoulombIds const& ids, Energy cutoff) const { real_type inc_energy = value_as(particle.energy()); real_type mass = value_as(particle.mass()); - real_type max_energy = particle.particle_id() == data.ids.electron + real_type max_energy = particle.particle_id() == ids.electron ? real_type{0.5} * inc_energy : inc_energy; real_type final_energy = inc_energy @@ -210,5 +319,26 @@ WentzelHelper::calc_costheta_max_electron(ParticleTrackView const& particle, return 0; } +//---------------------------------------------------------------------------// +/*! + * Calculate the maximum scattering angle off the target nucleus. + */ +CELER_FUNCTION real_type WentzelHelper::calc_cos_thetamax_nuclear( + ParticleTrackView const& particle, + MaterialView const& material, + NativeCRef const& wentzel) const +{ + if (wentzel.params.is_combined) + { + CELER_ASSERT(material.material_id() < wentzel.inv_mass_cbrt_sq.size()); + return max(wentzel.params.costheta_limit, + 1 + - wentzel.params.a_sq_factor + * wentzel.inv_mass_cbrt_sq[material.material_id()] + / value_as(particle.momentum_sq())); + } + return wentzel.params.costheta_limit; +} + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/em/xs/WentzelTransportXsCalculator.hh b/src/celeritas/em/xs/WentzelTransportXsCalculator.hh index fa8e92b0b8..5bef790a91 100644 --- a/src/celeritas/em/xs/WentzelTransportXsCalculator.hh +++ b/src/celeritas/em/xs/WentzelTransportXsCalculator.hh @@ -30,7 +30,6 @@ class WentzelTransportXsCalculator //!@{ //! \name Type aliases using XsUnits = units::Native; // [len^2] - using Charge = units::ElementaryCharge; using Mass = units::MevMass; using MomentumSq = units::MevMomentumSq; //!@} @@ -42,26 +41,23 @@ class WentzelTransportXsCalculator WentzelHelper const& helper); // Calculate the transport cross section for the given angle [len^2] - inline CELER_FUNCTION real_type operator()(real_type costheta_max) const; + inline CELER_FUNCTION real_type operator()(real_type cos_thetamax) const; private: //// DATA //// AtomicNumber z_; real_type screening_coeff_; - real_type costheta_max_elec_; + real_type cos_thetamax_elec_; real_type beta_sq_; - real_type xs_factor_; + real_type kin_factor_; //// HELPER FUNCTIONS //// - // Calculate the multiplicative factor for the transport cross section - real_type calc_xs_factor(ParticleTrackView const&) const; - // Calculate xs contribution from scattering off electrons or nucleus - real_type calc_xs_contribution(real_type costheta_max) const; + real_type calc_xs_contribution(real_type cos_thetamax) const; - //! Limit on (1 - \c costheta_max) / \c screening_coeff + //! Limit on (1 - \c cos_thetamax) / \c screening_coeff static CELER_CONSTEXPR_FUNCTION real_type limit() { return 0.1; } }; @@ -72,7 +68,7 @@ class WentzelTransportXsCalculator * Construct with particle and precalculatad Wentzel data. * * \c beta_sq should be calculated from the incident particle energy and mass. - * \c screening_coeff and \c costheta_max_elec are calculated using the Wentzel + * \c screening_coeff and \c cos_thetamax_elec are calculated using the Wentzel * OK and VI model in \c WentzelHelper and depend on properties of the incident * particle, the energy cutoff in the current material, and the target element. */ @@ -81,9 +77,9 @@ WentzelTransportXsCalculator::WentzelTransportXsCalculator( ParticleTrackView const& particle, WentzelHelper const& helper) : z_(helper.atomic_number()) , screening_coeff_(2 * helper.screening_coefficient()) - , costheta_max_elec_(helper.costheta_max_electron()) + , cos_thetamax_elec_(helper.cos_thetamax_electron()) , beta_sq_(particle.beta_sq()) - , xs_factor_(this->calc_xs_factor(particle)) + , kin_factor_(helper.kin_factor()) { } @@ -92,56 +88,31 @@ WentzelTransportXsCalculator::WentzelTransportXsCalculator( * Calculate the transport cross section for the given angle [len^2]. */ CELER_FUNCTION real_type -WentzelTransportXsCalculator::operator()(real_type costheta_max) const +WentzelTransportXsCalculator::operator()(real_type cos_thetamax) const { - CELER_EXPECT(costheta_max <= 1); + CELER_EXPECT(cos_thetamax <= 1); // Sum xs contributions from scattering off electrons and nucleus - real_type xs_nuc = this->calc_xs_contribution(costheta_max); - real_type xs_elec = costheta_max_elec_ > costheta_max - ? this->calc_xs_contribution(costheta_max_elec_) + real_type xs_nuc = this->calc_xs_contribution(cos_thetamax); + real_type xs_elec = cos_thetamax_elec_ > cos_thetamax + ? this->calc_xs_contribution(cos_thetamax_elec_) : xs_nuc; - real_type result = xs_factor_ * (xs_elec + z_.get() * xs_nuc); + real_type result = kin_factor_ * (xs_elec + z_.get() * xs_nuc); CELER_ENSURE(result >= 0); return result; } -//---------------------------------------------------------------------------// -/*! - * Calculate the multiplicative factor for the transport cross section. - * - * This calculates the factor - * \f[ - f = \frac{2 \pi m_e^2 r_e^2 Z q^2}{\beta^2 p^2}, - * \f] - * where \f$ m_e, r_e, Z, q, \beta \f$, and \f$ p \f$ are the electron mass, - * classical electron radius, atomic number of the target atom, charge, - * relativistic speed, and momentum of the incident particle, respectively. - */ -CELER_FUNCTION real_type WentzelTransportXsCalculator::calc_xs_factor( - ParticleTrackView const& particle) const -{ - real_type constexpr twopi_mrsq - = 2 * constants::pi - * ipow<2>(native_value_to(constants::electron_mass).value() - * constants::r_electron); - - return twopi_mrsq * z_.get() * ipow<2>(value_as(particle.charge())) - / (particle.beta_sq() - * value_as(particle.momentum_sq())); -} - //---------------------------------------------------------------------------// /*! * Calculate contribution to xs from scattering off electrons or nucleus. */ CELER_FUNCTION real_type WentzelTransportXsCalculator::calc_xs_contribution( - real_type costheta_max) const + real_type cos_thetamax) const { real_type result; real_type const spin = real_type(0.5); - real_type x = (1 - costheta_max) / screening_coeff_; + real_type x = (1 - cos_thetamax) / screening_coeff_; if (x < WentzelTransportXsCalculator::limit()) { real_type x_sq = ipow<2>(x); diff --git a/src/celeritas/ext/GeantImporter.cc b/src/celeritas/ext/GeantImporter.cc index d28bf1a4a1..a7b9f6e445 100644 --- a/src/celeritas/ext/GeantImporter.cc +++ b/src/celeritas/ext/GeantImporter.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -327,6 +328,28 @@ to_msc_step_algorithm(G4MscStepLimitType const& msc_step_algorithm) CELER_ASSERT_UNREACHABLE(); } +//---------------------------------------------------------------------------// +/*! + * Safely switch from G4NuclearFormfactorType [G4NuclearFormfactorType.hh] to + * NuclearFormFactorType. + */ +NuclearFormFactorType +to_form_factor_type(G4NuclearFormfactorType const& form_factor_type) +{ + switch (form_factor_type) + { + case G4NuclearFormfactorType::fNoneNF: + return NuclearFormFactorType::none; + case G4NuclearFormfactorType::fExponentialNF: + return NuclearFormFactorType::exponential; + case G4NuclearFormfactorType::fGaussianNF: + return NuclearFormFactorType::gaussian; + case G4NuclearFormfactorType::fFlatNF: + return NuclearFormFactorType::flat; + } + CELER_ASSERT_UNREACHABLE(); +} + //---------------------------------------------------------------------------// /*! * Return a populated \c ImportParticle vector. @@ -919,8 +942,11 @@ ImportEmParameters import_em_parameters() #else CELER_DISCARD(len_scale); #endif + import.msc_theta_limit = g4.MscThetaLimit(); + import.angle_limit_factor = g4.FactorForAngleLimit(); import.apply_cuts = g4.ApplyCuts(); import.screening_factor = g4.ScreeningFactor(); + import.form_factor = to_form_factor_type(g4.NuclearFormfactorType()); CELER_ENSURE(import); return import; diff --git a/src/celeritas/ext/GeantPhysicsOptions.hh b/src/celeritas/ext/GeantPhysicsOptions.hh index 30a1fb0f87..1160cb02b7 100644 --- a/src/celeritas/ext/GeantPhysicsOptions.hh +++ b/src/celeritas/ext/GeantPhysicsOptions.hh @@ -8,6 +8,7 @@ #pragma once #include "corecel/Types.hh" +#include "celeritas/Constants.hh" #include "celeritas/Quantities.hh" namespace celeritas @@ -121,8 +122,14 @@ struct GeantPhysicsOptions double msc_safety_factor{0.6}; //! Lambda limit for MSC models [len] double msc_lambda_limit{0.1 * units::centimeter}; + //! Polar angle limii between single and multiple Coulomb scattering + double msc_theta_limit{constants::pi}; + //! Factor for dynamic computation of angular limit between SS and MSC + double angle_limit_factor{1}; //! Step limit algorithm for MSC models MscStepLimitAlgorithm msc_step_algorithm{MscStepLimitAlgorithm::safety}; + //! Nuclear form factor model for Coulomm scattering + NuclearFormFactorType form_factor{NuclearFormFactorType::exponential}; //!@} //! Print detailed Geant4 output @@ -158,7 +165,10 @@ operator==(GeantPhysicsOptions const& a, GeantPhysicsOptions const& b) && a.msc_range_factor == b.msc_range_factor && a.msc_safety_factor == b.msc_safety_factor && a.msc_lambda_limit == b.msc_lambda_limit + && a.msc_theta_limit == b.msc_theta_limit + && a.angle_limit_factor == b.angle_limit_factor && a.msc_step_algorithm == b.msc_step_algorithm + && a.form_factor == b.form_factor && a.verbose == b.verbose; // clang-format on } diff --git a/src/celeritas/ext/GeantPhysicsOptionsIO.json.cc b/src/celeritas/ext/GeantPhysicsOptionsIO.json.cc index 01002d2040..bc7cd6acbe 100644 --- a/src/celeritas/ext/GeantPhysicsOptionsIO.json.cc +++ b/src/celeritas/ext/GeantPhysicsOptionsIO.json.cc @@ -70,6 +70,19 @@ void to_json(nlohmann::json& j, MscStepLimitAlgorithm const& value) j = std::string{to_cstring(value)}; } +void from_json(nlohmann::json const& j, NuclearFormFactorType& value) +{ + static auto const from_string + = StringEnumMapper::from_cstring_func( + to_cstring, "form factor"); + value = from_string(j.get()); +} + +void to_json(nlohmann::json& j, NuclearFormFactorType const& value) +{ + j = std::string{to_cstring(value)}; +} + //---------------------------------------------------------------------------// /*! * Read options from JSON. @@ -108,7 +121,10 @@ void from_json(nlohmann::json const& j, GeantPhysicsOptions& options) GPO_LOAD_OPTION(msc_range_factor); GPO_LOAD_OPTION(msc_safety_factor); GPO_LOAD_OPTION(msc_lambda_limit); + GPO_LOAD_OPTION(msc_theta_limit); + GPO_LOAD_OPTION(angle_limit_factor); GPO_LOAD_OPTION(msc_step_algorithm); + GPO_LOAD_OPTION(form_factor); GPO_LOAD_OPTION(verbose); #undef GPO_LOAD_OPTION @@ -152,7 +168,10 @@ void to_json(nlohmann::json& j, GeantPhysicsOptions const& options) GPO_SAVE_OPTION(msc_range_factor); GPO_SAVE_OPTION(msc_safety_factor); GPO_SAVE_OPTION(msc_lambda_limit); + GPO_SAVE_OPTION(msc_theta_limit); + GPO_SAVE_OPTION(angle_limit_factor); GPO_SAVE_OPTION(msc_step_algorithm); + GPO_SAVE_OPTION(form_factor); GPO_SAVE_OPTION(verbose); #undef GPO_SAVE_OPTION diff --git a/src/celeritas/ext/detail/CelerEmStandardPhysics.cc b/src/celeritas/ext/detail/CelerEmStandardPhysics.cc index 9bc7eba8aa..5038ebd257 100644 --- a/src/celeritas/ext/detail/CelerEmStandardPhysics.cc +++ b/src/celeritas/ext/detail/CelerEmStandardPhysics.cc @@ -69,6 +69,28 @@ from_msc_step_algorithm(MscStepLimitAlgorithm const& msc_step_algorithm) } } +//---------------------------------------------------------------------------// +/*! + * Safely switch from NuclearFormFactorType to G4NuclearFormfactorType. + */ +G4NuclearFormfactorType +from_form_factor_type(NuclearFormFactorType const& form_factor) +{ + switch (form_factor) + { + case NuclearFormFactorType::none: + return G4NuclearFormfactorType::fNoneNF; + case NuclearFormFactorType::exponential: + return G4NuclearFormfactorType::fExponentialNF; + case NuclearFormFactorType::gaussian: + return G4NuclearFormfactorType::fGaussianNF; + case NuclearFormFactorType::flat: + return G4NuclearFormfactorType::fFlatNF; + default: + CELER_ASSERT_UNREACHABLE(); + } +} + //---------------------------------------------------------------------------// /*! * Construct with physics options. @@ -93,6 +115,8 @@ CelerEmStandardPhysics::CelerEmStandardPhysics(Options const& options) em_parameters.SetAuger(options.relaxation == RelaxationSelection::all); em_parameters.SetIntegral(options.integral_approach); em_parameters.SetLinearLossLimit(options.linear_loss_limit); + em_parameters.SetNuclearFormfactorType( + from_form_factor_type(options.form_factor)); em_parameters.SetMscStepLimitType( from_msc_step_algorithm(options.msc_step_algorithm)); em_parameters.SetMscRangeFactor(options.msc_range_factor); @@ -105,6 +129,7 @@ CelerEmStandardPhysics::CelerEmStandardPhysics(Options const& options) em_parameters.SetMscLambdaLimit( native_value_to(options.msc_lambda_limit).value()); #endif + em_parameters.SetMscThetaLimit(options.msc_theta_limit); em_parameters.SetLowestElectronEnergy( value_as(options.lowest_electron_energy) * CLHEP::MeV); diff --git a/src/celeritas/global/CoreParams.cc b/src/celeritas/global/CoreParams.cc index e8355b9687..1f6b9e6a40 100644 --- a/src/celeritas/global/CoreParams.cc +++ b/src/celeritas/global/CoreParams.cc @@ -23,6 +23,7 @@ #include "corecel/sys/MemRegistry.hh" #include "corecel/sys/ScopedMem.hh" #include "geocel/GeoParamsOutput.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/geo/GeoMaterialParams.hh" // IWYU pragma: keep #include "celeritas/geo/GeoParams.hh" // IWYU pragma: keep #include "celeritas/geo/detail/BoundaryAction.hh" @@ -88,6 +89,10 @@ build_params_refs(CoreParams::Input const& p, CoreScalars const& scalars) ref.rng = get_ref(*p.rng); ref.sim = get_ref(*p.sim); ref.init = get_ref(*p.init); + if (p.wentzel) + { + ref.wentzel = get_ref(*p.wentzel); + } CELER_ENSURE(ref); return ref; diff --git a/src/celeritas/global/CoreParams.hh b/src/celeritas/global/CoreParams.hh index ac1ebce6bc..2e39e9171a 100644 --- a/src/celeritas/global/CoreParams.hh +++ b/src/celeritas/global/CoreParams.hh @@ -23,9 +23,7 @@ namespace celeritas { //---------------------------------------------------------------------------// class ActionRegistry; -class AtomicRelaxationParams; class CutoffParams; -class FluctuationParams; class GeoMaterialParams; class MaterialParams; class OutputRegistry; @@ -33,6 +31,7 @@ class ParticleParams; class PhysicsParams; class SimParams; class TrackInitParams; +class WentzelOKVIParams; //---------------------------------------------------------------------------// /*! @@ -52,6 +51,7 @@ class CoreParams final : public ParamsDataInterface using SPConstRng = std::shared_ptr; using SPConstSim = std::shared_ptr; using SPConstTrackInit = std::shared_ptr; + using SPConstWentzelOKVI = std::shared_ptr; using SPActionRegistry = std::shared_ptr; using SPOutputRegistry = std::shared_ptr; @@ -72,6 +72,7 @@ class CoreParams final : public ParamsDataInterface SPConstRng rng; SPConstSim sim; SPConstTrackInit init; + SPConstWentzelOKVI wentzel; SPActionRegistry action_reg; SPOutputRegistry output_reg; @@ -106,6 +107,7 @@ class CoreParams final : public ParamsDataInterface SPConstRng const& rng() const { return input_.rng; } SPConstSim const& sim() const { return input_.sim; } SPConstTrackInit const& init() const { return input_.init; } + SPConstWentzelOKVI const& wentzel() const { return input_.wentzel; } SPActionRegistry const& action_reg() const { return input_.action_reg; } SPOutputRegistry const& output_reg() const { return input_.output_reg; } //!@} diff --git a/src/celeritas/global/CoreTrackData.hh b/src/celeritas/global/CoreTrackData.hh index 0fa21a26a2..46f2abac39 100644 --- a/src/celeritas/global/CoreTrackData.hh +++ b/src/celeritas/global/CoreTrackData.hh @@ -10,6 +10,7 @@ #include "corecel/Assert.hh" #include "corecel/data/Collection.hh" #include "celeritas/Types.hh" +#include "celeritas/em/data/WentzelOKVIData.hh" #include "celeritas/geo/GeoData.hh" #include "celeritas/geo/GeoMaterialData.hh" #include "celeritas/mat/MaterialData.hh" @@ -65,6 +66,7 @@ struct CoreParamsData RngParamsData rng; SimParamsData sim; TrackInitParamsData init; + WentzelOKVIData wentzel; CoreScalars scalars; @@ -89,6 +91,7 @@ struct CoreParamsData rng = other.rng; sim = other.sim; init = other.init; + wentzel = other.wentzel; scalars = other.scalars; return *this; } diff --git a/src/celeritas/io/ImportData.cc b/src/celeritas/io/ImportData.cc index 798c2565e8..10a8932e77 100644 --- a/src/celeritas/io/ImportData.cc +++ b/src/celeritas/io/ImportData.cc @@ -7,6 +7,8 @@ //---------------------------------------------------------------------------// #include "ImportData.hh" +#include + #include "corecel/Assert.hh" #include "corecel/io/Logger.hh" #include "celeritas/UnitTypes.hh" @@ -49,5 +51,36 @@ void convert_to_native(ImportData* data) CELER_ENSURE(data->units == units::NativeTraits::label()); } +//---------------------------------------------------------------------------// +/*! + * Whether an imported model of the given class is present. + */ +bool has_model(ImportData const& data, ImportModelClass model_class) +{ + for (ImportProcess const& process : data.processes) + { + for (ImportModel const& model : process.models) + { + if (model.model_class == model_class) + { + return true; + } + } + } + return false; +} + +//---------------------------------------------------------------------------// +/*! + * Whether an imported MSC model of the given class is present. + */ +bool has_msc_model(ImportData const& data, ImportModelClass model_class) +{ + return std::any_of( + data.msc_models.begin(), + data.msc_models.end(), + [&](ImportMscModel const& m) { return m.model_class == model_class; }); +} + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/io/ImportData.hh b/src/celeritas/io/ImportData.hh index dc3d3713ea..a56b53c1c9 100644 --- a/src/celeritas/io/ImportData.hh +++ b/src/celeritas/io/ImportData.hh @@ -88,5 +88,11 @@ struct ImportData // Recursively convert imported data to the native unit type void convert_to_native(ImportData* data); +// Whether an imported model of the given class is present +bool has_model(ImportData const&, ImportModelClass); + +// Whether an imported MSC model of the given class is present +bool has_msc_model(ImportData const&, ImportModelClass); + //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/io/ImportParameters.hh b/src/celeritas/io/ImportParameters.hh index 7d03db176d..6dfdde7e2d 100644 --- a/src/celeritas/io/ImportParameters.hh +++ b/src/celeritas/io/ImportParameters.hh @@ -9,6 +9,7 @@ #include +#include "celeritas/Constants.hh" #include "celeritas/Types.hh" #include "celeritas/Units.hh" @@ -52,10 +53,16 @@ struct ImportEmParameters double msc_safety_factor{0.6}; //! MSC lambda limit [length] double msc_lambda_limit{1 * units::millimeter}; + //! Polar angle limii between single and multiple Coulomb scattering + double msc_theta_limit{constants::pi}; //! Kill secondaries below production cut bool apply_cuts{false}; //! Nuclear screening factor for single/multiple Coulomb scattering double screening_factor{1}; + //! Factor for dynamic computation of angular limit between SS and MSC + double angle_limit_factor{1}; + //! Nuclear form factor model for Coulomm scattering + NuclearFormFactorType form_factor{NuclearFormFactorType::exponential}; //! Whether parameters are assigned and valid explicit operator bool() const @@ -64,7 +71,9 @@ struct ImportEmParameters && msc_step_algorithm != MscStepLimitAlgorithm::size_ && msc_range_factor > 0 && msc_range_factor < 1 && msc_safety_factor >= 0.1 && msc_lambda_limit > 0 - && screening_factor > 0; + && msc_theta_limit >= 0 && msc_theta_limit <= constants::pi + && screening_factor > 0 && angle_limit_factor > 0 + && form_factor != NuclearFormFactorType::size_; } }; diff --git a/src/celeritas/phys/ProcessBuilder.cc b/src/celeritas/phys/ProcessBuilder.cc index 9beb3ee70d..2a787d0fc7 100644 --- a/src/celeritas/phys/ProcessBuilder.cc +++ b/src/celeritas/phys/ProcessBuilder.cc @@ -68,7 +68,6 @@ ProcessBuilder::ProcessBuilder(ImportData const& data, , brem_combined_(options.brem_combined) , enable_lpm_(data.em_params.lpm) , use_integral_xs_(data.em_params.integral_approach) - , coulomb_screening_factor_(data.em_params.screening_factor) { CELER_EXPECT(input_.material); CELER_EXPECT(input_.particle); @@ -241,12 +240,11 @@ auto ProcessBuilder::build_annihilation() -> SPProcess //---------------------------------------------------------------------------// auto ProcessBuilder::build_coulomb() -> SPProcess { - CoulombScatteringModel::Options options; - options.screening_factor = coulomb_screening_factor_; + CoulombScatteringProcess::Options options; options.use_integral_xs = use_integral_xs_; return std::make_shared( - this->particle(), this->material(), this->imported(), options); + this->particle(), this->imported(), options); } //---------------------------------------------------------------------------// diff --git a/src/celeritas/phys/ProcessBuilder.hh b/src/celeritas/phys/ProcessBuilder.hh index b2fed05681..392ed070b1 100644 --- a/src/celeritas/phys/ProcessBuilder.hh +++ b/src/celeritas/phys/ProcessBuilder.hh @@ -120,7 +120,6 @@ class ProcessBuilder bool brem_combined_; bool enable_lpm_; bool use_integral_xs_; - real_type coulomb_screening_factor_; //// HELPER FUNCTIONS //// diff --git a/test/celeritas/GlobalTestBase.cc b/test/celeritas/GlobalTestBase.cc index d5d113e26a..3e2949e974 100644 --- a/test/celeritas/GlobalTestBase.cc +++ b/test/celeritas/GlobalTestBase.cc @@ -94,6 +94,7 @@ auto GlobalTestBase::build_core() -> SPConstCore inp.rng = this->rng(); inp.sim = this->sim(); inp.init = this->init(); + inp.wentzel = this->wentzel(); inp.action_reg = this->action_reg(); inp.output_reg = this->output_reg(); CELER_ASSERT(inp); diff --git a/test/celeritas/GlobalTestBase.hh b/test/celeritas/GlobalTestBase.hh index e425f21226..b5a6639887 100644 --- a/test/celeritas/GlobalTestBase.hh +++ b/test/celeritas/GlobalTestBase.hh @@ -31,6 +31,7 @@ class ParticleParams; class PhysicsParams; class SimParams; class TrackInitParams; +class WentzelOKVIParams; class CoreParams; class OutputRegistry; @@ -67,6 +68,7 @@ class GlobalTestBase : public Test using SPConstRng = SP; using SPConstSim = SP; using SPConstTrackInit = SP; + using SPConstWentzelOKVI = SP; using SPConstCore = SP; using SPActionRegistry = SP; @@ -97,6 +99,7 @@ class GlobalTestBase : public Test inline SPConstRng const& rng(); inline SPConstSim const& sim(); inline SPConstTrackInit const& init(); + inline SPConstWentzelOKVI const& wentzel(); inline SPActionRegistry const& action_reg(); inline SPConstCore const& core(); inline SPConstCerenkov const& cerenkov(); @@ -113,6 +116,7 @@ class GlobalTestBase : public Test inline SPConstRng const& rng() const; inline SPConstSim const& sim() const; inline SPConstTrackInit const& init() const; + inline SPConstWentzelOKVI const& wentzel() const; inline SPActionRegistry const& action_reg() const; inline SPConstCore const& core() const; inline SPConstCerenkov const& cerenkov() const; @@ -138,6 +142,7 @@ class GlobalTestBase : public Test [[nodiscard]] virtual SPConstPhysics build_physics() = 0; [[nodiscard]] virtual SPConstSim build_sim() = 0; [[nodiscard]] virtual SPConstTrackInit build_init() = 0; + [[nodiscard]] virtual SPConstWentzelOKVI build_wentzel() = 0; [[nodiscard]] virtual SPConstAction build_along_step() = 0; [[nodiscard]] virtual SPConstCerenkov build_cerenkov() = 0; [[nodiscard]] virtual SPConstProperties build_properties() = 0; @@ -160,6 +165,7 @@ class GlobalTestBase : public Test SPConstRng rng_; SPConstSim sim_; SPConstTrackInit init_; + SPConstWentzelOKVI wentzel_; SPConstCore core_; SPOutputRegistry output_reg_; SPConstCerenkov cerenkov_; @@ -202,6 +208,18 @@ DEF_GTB_ACCESSORS(SPConstCore, core) DEF_GTB_ACCESSORS(SPConstCerenkov, cerenkov) DEF_GTB_ACCESSORS(SPConstProperties, properties) DEF_GTB_ACCESSORS(SPConstScintillation, scintillation) +auto GlobalTestBase::wentzel() -> SPConstWentzelOKVI const& +{ + if (!this->wentzel_) + { + this->wentzel_ = this->build_wentzel(); + } + return this->wentzel_; +} +auto GlobalTestBase::wentzel() const -> SPConstWentzelOKVI const& +{ + return this->wentzel_; +} #undef DEF_GTB_ACCESSORS diff --git a/test/celeritas/ImportedDataTestBase.cc b/test/celeritas/ImportedDataTestBase.cc index ae2e788cb7..c2f45c7e4b 100644 --- a/test/celeritas/ImportedDataTestBase.cc +++ b/test/celeritas/ImportedDataTestBase.cc @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #include "ImportedDataTestBase.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/geo/GeoMaterialParams.hh" #include "celeritas/io/ImportData.hh" #include "celeritas/mat/MaterialParams.hh" @@ -70,6 +71,13 @@ auto ImportedDataTestBase::build_sim() -> SPConstSim return SimParams::from_import(this->imported_data(), this->particle()); } +//---------------------------------------------------------------------------// +auto ImportedDataTestBase::build_wentzel() -> SPConstWentzelOKVI +{ + return WentzelOKVIParams::from_import(this->imported_data(), + this->material()); +} + //---------------------------------------------------------------------------// auto ImportedDataTestBase::build_physics() -> SPConstPhysics { diff --git a/test/celeritas/ImportedDataTestBase.hh b/test/celeritas/ImportedDataTestBase.hh index a18c91cf44..fb10e6ae0d 100644 --- a/test/celeritas/ImportedDataTestBase.hh +++ b/test/celeritas/ImportedDataTestBase.hh @@ -50,6 +50,7 @@ class ImportedDataTestBase : virtual public GlobalGeoTestBase SPConstCutoff build_cutoff() override; SPConstPhysics build_physics() override; SPConstSim build_sim() override; + SPConstWentzelOKVI build_wentzel() override; SPConstCerenkov build_cerenkov() override; SPConstProperties build_properties() override; SPConstScintillation build_scintillation() override; diff --git a/test/celeritas/MockTestBase.hh b/test/celeritas/MockTestBase.hh index 9d82e7610c..7812a319b7 100644 --- a/test/celeritas/MockTestBase.hh +++ b/test/celeritas/MockTestBase.hh @@ -80,6 +80,7 @@ class MockTestBase : virtual public GlobalGeoTestBase, public OnlyCoreTestBase SPConstAction build_along_step() override; SPConstSim build_sim() override; SPConstTrackInit build_init() override; + SPConstWentzelOKVI build_wentzel() override { return nullptr; } virtual PhysicsOptions build_physics_options() const; diff --git a/test/celeritas/OnlyGeoTestBase.hh b/test/celeritas/OnlyGeoTestBase.hh index f8ed554a66..a17f44f798 100644 --- a/test/celeritas/OnlyGeoTestBase.hh +++ b/test/celeritas/OnlyGeoTestBase.hh @@ -33,6 +33,7 @@ class OnlyGeoTestBase : virtual public GlobalTestBase { CELER_ASSERT_UNREACHABLE(); } + SPConstWentzelOKVI build_wentzel() override { CELER_ASSERT_UNREACHABLE(); } }; //---------------------------------------------------------------------------// } // namespace test diff --git a/test/celeritas/SimpleTestBase.cc b/test/celeritas/SimpleTestBase.cc index 335e6c81ea..5f67db7075 100644 --- a/test/celeritas/SimpleTestBase.cc +++ b/test/celeritas/SimpleTestBase.cc @@ -8,6 +8,7 @@ #include "SimpleTestBase.hh" #include "celeritas/Quantities.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/em/process/ComptonProcess.hh" #include "celeritas/geo/GeoMaterialParams.hh" #include "celeritas/global/ActionRegistry.hh" @@ -181,6 +182,13 @@ auto SimpleTestBase::build_init() -> SPConstTrackInit return std::make_shared(input); } +//---------------------------------------------------------------------------// +auto SimpleTestBase::build_wentzel() -> SPConstWentzelOKVI +{ + WentzelOKVIParams::Options options; + return std::make_shared(this->material(), options); +} + //---------------------------------------------------------------------------// auto SimpleTestBase::build_along_step() -> SPConstAction { diff --git a/test/celeritas/SimpleTestBase.hh b/test/celeritas/SimpleTestBase.hh index 05589b1542..6fb81b3953 100644 --- a/test/celeritas/SimpleTestBase.hh +++ b/test/celeritas/SimpleTestBase.hh @@ -35,6 +35,7 @@ class SimpleTestBase : virtual public GlobalGeoTestBase, public OnlyCoreTestBase SPConstSim build_sim() override; SPConstTrackInit build_init() override; SPConstAction build_along_step() override; + SPConstWentzelOKVI build_wentzel() override; }; //---------------------------------------------------------------------------// diff --git a/test/celeritas/em/CoulombScattering.test.cc b/test/celeritas/em/CoulombScattering.test.cc index 6a75cf15be..bd28a14832 100644 --- a/test/celeritas/em/CoulombScattering.test.cc +++ b/test/celeritas/em/CoulombScattering.test.cc @@ -9,6 +9,7 @@ #include "celeritas/Units.hh" #include "celeritas/em/interactor/CoulombScatteringInteractor.hh" #include "celeritas/em/model/CoulombScatteringModel.hh" +#include "celeritas/em/params/WentzelOKVIParams.hh" #include "celeritas/em/process/CoulombScatteringProcess.hh" #include "celeritas/em/xs/WentzelTransportXsCalculator.hh" #include "celeritas/io/ImportParameters.hh" @@ -85,15 +86,15 @@ class CoulombScatteringTest : public InteractorHostTestBase {std::move(ip_electron), std::move(ip_positron)}); } - // Use default options - CoulombScatteringModel::Options options; + // Default to single scattering + WentzelOKVIParams::Options options; + options.is_combined = false; + options.polar_angle_limit = 0; + wentzel_ = std::make_shared(this->material_params(), + options); model_ = std::make_shared( - ActionId{0}, - *this->particle_params(), - *this->material_params(), - options, - this->imported_processes()); + ActionId{0}, *this->particle_params(), this->imported_processes()); // Set cutoffs CutoffParams::Input input; @@ -132,63 +133,113 @@ class CoulombScatteringTest : public InteractorHostTestBase } protected: + std::shared_ptr wentzel_; std::shared_ptr model_; + IsotopeComponentId isocomp_id_{0}; + ElementComponentId elcomp_id_{0}; + ElementId el_id_{0}; + MaterialId mat_id_{0}; }; -TEST_F(CoulombScatteringTest, wokvi_xs) +TEST_F(CoulombScatteringTest, helper) { - CoulombScatteringHostRef const& data = model_->host_ref(); - + struct Result + { + using VecReal = std::vector; + + VecReal screen_z; + VecReal scaled_kin_factor; + VecReal cos_thetamax_elec; + VecReal cos_thetamax_nuc; + VecReal xs_elec; + VecReal xs_nuc; + VecReal xs_ratio; + }; + + auto const material = this->material_track().make_material_view(); AtomicNumber const target_z - = this->material_params()->get(ElementId{0}).atomic_number(); + = this->material_params()->get(el_id_).atomic_number(); - MevEnergy const cutoff = this->cutoff_params() - ->get(MaterialId{0}) - .energy(this->particle_track().particle_id()); + MevEnergy const cutoff = this->cutoff_params()->get(mat_id_).energy( + this->particle_track().particle_id()); - std::vector const energies = {50, 100, 200, 1000, 13000}; + real_type const cos_thetamax = model_->host_ref().cos_thetamax(); - static real_type const expected_screen_z[] = {2.1181757502465e-08, - 5.3641196710457e-09, - 1.3498490873627e-09, - 5.4280909096648e-11, - 3.2158426877075e-13}; + Result result; + for (real_type energy : {50, 100, 200, 1000, 13000}) + { + this->set_inc_particle(pdg::electron(), MevEnergy{energy}); - static real_type const expected_cos_t_max[] = {0.99989885103277, - 0.99997458240728, - 0.99999362912075, - 0.99999974463379, - 0.99999999848823}; + WentzelHelper helper(this->particle_track(), + material, + target_z, + wentzel_->host_ref(), + model_->host_ref().ids, + cutoff); + + EXPECT_SOFT_EQ(1.1682, helper.mott_factor()); + result.screen_z.push_back(helper.screening_coefficient()); + // Scale the xs factor by 1 / r_e^2 so the values will be large enough + // for the soft equivalence comparison to catch any differences + result.scaled_kin_factor.push_back(helper.kin_factor() + / ipow<2>(constants::r_electron)); + result.cos_thetamax_elec.push_back(helper.cos_thetamax_electron()); + real_type const cos_thetamax_nuc = helper.cos_thetamax_nuclear(); + result.cos_thetamax_nuc.push_back(cos_thetamax_nuc); + result.xs_elec.push_back( + helper.calc_xs_electron(cos_thetamax_nuc, cos_thetamax) + / units::barn); + result.xs_nuc.push_back( + helper.calc_xs_nuclear(cos_thetamax_nuc, cos_thetamax) + / units::barn); + result.xs_ratio.push_back( + helper.calc_xs_ratio(cos_thetamax_nuc, cos_thetamax)); + } - static real_type const expected_xsecs[] = {0.033319844069031, + static double const expected_screen_z[] = {2.1181757502465e-08, + 5.3641196710457e-09, + 1.3498490873627e-09, + 5.4280909096648e-11, + 3.2158426877075e-13}; + static double const expected_scaled_kin_factor[] = {0.018652406309778, + 0.0047099159161888, + 0.0011834423911797, + 4.7530717872407e-05, + 2.8151208086621e-07}; + static double const expected_cos_thetamax_elec[] = {0.99989885103277, + 0.99997458240728, + 0.99999362912075, + 0.99999974463379, + 0.99999999848823}; + static double const expected_cos_thetamax_nuc[] = {1, 1, 1, 1, 1}; + static double const expected_xs_elec[] = {40826.46816866, + 40708.229862005, + 40647.018860182, + 40596.955206725, + 40585.257368735}; + static double const expected_xs_nuc[] = {1184463.4246675, + 1181036.9405781, + 1179263.0534548, + 1177812.2021574, + 1177473.1955599}; + static double const expected_xs_ratio[] = {0.033319844069031, 0.033319738720425, 0.033319684608429, 0.033319640583261, 0.03331963032739}; - - std::vector xsecs, cos_t_maxs, screen_zs; - for (real_type energy : energies) - { - this->set_inc_particle(pdg::electron(), MevEnergy{energy}); - - WentzelHelper helper(this->particle_track(), target_z, data, cutoff); - - xsecs.push_back(helper.calc_xs_ratio()); - cos_t_maxs.push_back(helper.costheta_max_electron()); - screen_zs.push_back(helper.screening_coefficient()); - } - - EXPECT_VEC_SOFT_EQ(expected_xsecs, xsecs); - EXPECT_VEC_SOFT_EQ(expected_screen_z, screen_zs); - EXPECT_VEC_SOFT_EQ(expected_cos_t_max, cos_t_maxs); + EXPECT_VEC_SOFT_EQ(expected_screen_z, result.screen_z); + EXPECT_VEC_SOFT_EQ(expected_scaled_kin_factor, result.scaled_kin_factor); + EXPECT_VEC_SOFT_EQ(expected_cos_thetamax_elec, result.cos_thetamax_elec); + EXPECT_VEC_SOFT_EQ(expected_cos_thetamax_nuc, result.cos_thetamax_nuc); + EXPECT_VEC_SOFT_EQ(expected_xs_elec, result.xs_elec); + EXPECT_VEC_SOFT_EQ(expected_xs_nuc, result.xs_nuc); + EXPECT_VEC_SOFT_EQ(expected_xs_ratio, result.xs_ratio); } TEST_F(CoulombScatteringTest, mott_xs) { - CoulombScatteringHostRef const& data = model_->host_ref(); - - CoulombScatteringElementData const& element_data - = data.elem_data[ElementId(0)]; + MottElementData const& element_data + = wentzel_->host_ref().elem_data[el_id_]; MottRatioCalculator xsec(element_data, sqrt(this->particle_track().beta_sq())); @@ -216,14 +267,11 @@ TEST_F(CoulombScatteringTest, mott_xs) TEST_F(CoulombScatteringTest, wokvi_transport_xs) { - // Copper - MaterialId mat_id{0}; - ElementId elm_id{0}; - - AtomicNumber const z = this->material_params()->get(elm_id).atomic_number(); + auto const material = this->material_track().make_material_view(); + AtomicNumber const z = this->material_params()->get(el_id_).atomic_number(); // Incident particle energy cutoff - MevEnergy const cutoff = this->cutoff_params()->get(mat_id).energy( + MevEnergy const cutoff = this->cutoff_params()->get(mat_id_).energy( this->particle_track().particle_id()); std::vector xs; @@ -232,13 +280,18 @@ TEST_F(CoulombScatteringTest, wokvi_transport_xs) this->set_inc_particle(pdg::electron(), MevEnergy{energy}); auto const& particle = this->particle_track(); - WentzelHelper helper(particle, z, model_->host_ref(), cutoff); + WentzelHelper helper(particle, + material, + z, + wentzel_->host_ref(), + model_->host_ref().ids, + cutoff); WentzelTransportXsCalculator calc_transport_xs(particle, helper); - for (real_type costheta_max : {-1.0, -0.5, 0.0, 0.5, 0.75, 0.99, 1.0}) + for (real_type cos_thetamax : {-1.0, -0.5, 0.0, 0.5, 0.75, 0.99, 1.0}) { // Get cross section in barns - xs.push_back(calc_transport_xs(costheta_max) / units::barn); + xs.push_back(calc_transport_xs(cos_thetamax) / units::barn); } } static double const expected_xs[] = {0.18738907324438, @@ -283,11 +336,10 @@ TEST_F(CoulombScatteringTest, simple_scattering) { int const num_samples = 4; - IsotopeView const isotope = this->material_track() - .make_material_view() - .make_element_view(ElementComponentId{0}) - .make_isotope_view(IsotopeComponentId{0}); - auto cutoffs = this->cutoff_params()->get(MaterialId{0}); + auto const material = this->material_track().make_material_view(); + IsotopeView const isotope + = material.make_element_view(elcomp_id_).make_isotope_view(isocomp_id_); + auto cutoffs = this->cutoff_params()->get(mat_id_); RandomEngine& rng_engine = this->rng(); @@ -299,10 +351,12 @@ TEST_F(CoulombScatteringTest, simple_scattering) { this->set_inc_particle(pdg::electron(), MevEnergy{energy}); CoulombScatteringInteractor interact(model_->host_ref(), + wentzel_->host_ref(), this->particle_track(), this->direction(), + material, isotope, - ElementId{0}, + el_id_, cutoffs); for ([[maybe_unused]] int i : range(num_samples)) @@ -317,52 +371,52 @@ TEST_F(CoulombScatteringTest, simple_scattering) } } static double const expected_cos_theta[] = {1, - 0.9996601312603, - 0.99998628518524, - 0.9998960794451, - 1, - 0.99991152273528, - 0.99998247385882, - 0.99988427878015, + 0.99950360343422, + 0.98776892641281, + 0.99837727448607, 1, - 0.99999970191006, - 0.99999637934855, - 0.99999885954183, - 0.999999997443, - 0.99999999406847, - 0.99999999849197, + 0.9999716884097, + 0.99985707764428, + 0.99997835395879, 1, - 0.99999999992169, + 0.99999688465904, + 0.99999974351257, + 0.99999918571981, + 0.99999995498814, + 0.99999998059604, + 0.99999992367847, 1, - 0.99999999209019, - 0.99999999999489, + 0.99999999984949, 1, - 0.99999999999999, + 0.99999999999851, + 0.99999999769513, + 0.99999999999996, + 0.99999999999998, 0.99999999999999, 1}; static double const expected_delta_energy[] = {0, - 1.4170232209842e-09, - 5.7181509527382e-11, - 4.3327857968123e-10, + 2.069638599389e-09, + 5.0995313499724e-08, + 6.7656699409557e-09, 0, - 3.0519519134131e-09, - 6.0455007666604e-10, - 3.9917101846143e-09, + 9.7658547915103e-10, + 4.9299914151035e-09, + 7.4666273164326e-10, 0, - 5.6049742624964e-10, - 6.8078875870015e-09, - 2.1443966602419e-09, - 4.406643938637e-10, - 1.0222294122286e-09, - 2.5988811103161e-10, + 5.8577551698136e-09, + 4.8227200011297e-10, + 1.5310863688001e-09, + 7.7572508416779e-09, + 3.3440414881625e-09, + 1.315311237704e-08, 0, - 1.3371845852816e-09, + 2.5702320272103e-09, 0, - 1.350750835627e-07, - 8.7311491370201e-11, - 4.8021320253611e-10, - 8.7311491370201e-10, - 1.6734702512622e-09, + 2.5465851649642e-11, + 3.9359974834952e-08, + 7.1158865466714e-09, + 3.9435690268874e-09, + 2.0663719624281e-09, 0}; EXPECT_VEC_SOFT_EQ(expected_cos_theta, cos_theta); EXPECT_VEC_SOFT_EQ(expected_delta_energy, delta_energy); @@ -370,28 +424,33 @@ TEST_F(CoulombScatteringTest, simple_scattering) TEST_F(CoulombScatteringTest, distribution) { - CoulombScatteringHostRef const& data = model_->host_ref(); + auto const material = this->material_track().make_material_view(); + IsotopeView const isotope + = material.make_element_view(elcomp_id_).make_isotope_view(isocomp_id_); + + // TODO: Use proton ParticleId{2} + MevEnergy const cutoff + = this->cutoff_params()->get(mat_id_).energy(ParticleId{0}); + std::vector avg_angles; for (real_type energy : {1, 50, 100, 200, 1000, 13000}) { this->set_inc_particle(pdg::electron(), MevEnergy{energy}); - CoulombScatteringElementData const& element_data - = data.elem_data[ElementId(0)]; - - IsotopeView const isotope - = this->material_track() - .make_material_view() - .make_element_view(ElementComponentId{0}) - .make_isotope_view(IsotopeComponentId{0}); - - // TODO: Use proton ParticleId{2} - MevEnergy const cutoff - = this->cutoff_params()->get(MaterialId{0}).energy(ParticleId{0}); - - WentzelDistribution distrib( - this->particle_track(), isotope, element_data, cutoff, data); + WentzelHelper helper(this->particle_track(), + material, + isotope.atomic_number(), + wentzel_->host_ref(), + model_->host_ref().ids, + cutoff); + WentzelDistribution sample_angle(wentzel_->host_ref(), + helper, + this->particle_track(), + isotope, + el_id_, + helper.cos_thetamax_nuclear(), + model_->host_ref().cos_thetamax()); RandomEngine& rng_engine = this->rng(); @@ -400,19 +459,19 @@ TEST_F(CoulombScatteringTest, distribution) int const num_samples = 4096; for ([[maybe_unused]] int i : range(num_samples)) { - avg_angle += distrib(rng_engine); + avg_angle += sample_angle(rng_engine); } avg_angle /= num_samples; avg_angles.push_back(avg_angle); } - static double const expected_avg_angles[] = {0.99933909299229, - 0.99999960697043, - 0.99999986035881, - 0.99999998052954, - 0.99999999917037, - 0.9999999999969}; + static double const expected_avg_angles[] = {0.99957853627426, + 0.99999954645904, + 0.99999989882947, + 0.99999996985799, + 0.99999999945722, + 0.99999999999487}; EXPECT_VEC_SOFT_EQ(expected_avg_angles, avg_angles); } diff --git a/test/celeritas/ext/GeantImporter.test.cc b/test/celeritas/ext/GeantImporter.test.cc index d14b6f4ff7..773862826d 100644 --- a/test/celeritas/ext/GeantImporter.test.cc +++ b/test/celeritas/ext/GeantImporter.test.cc @@ -256,7 +256,7 @@ class FourSteelSlabsEmStandard : public GeantImporterTest { nlohmann::json out = opts; EXPECT_JSON_EQ( - R"json({"annihilation":true,"apply_cuts":false,"brems":"all","compton_scattering":true,"coulomb_scattering":false,"default_cutoff":0.1,"eloss_fluctuation":true,"em_bins_per_decade":7,"gamma_conversion":true,"gamma_general":false,"integral_approach":true,"ionization":true,"linear_loss_limit":0.01,"lowest_electron_energy":[0.001,"MeV"],"lpm":true,"max_energy":[100000000.0,"MeV"],"min_energy":[0.0001,"MeV"],"msc":"urban","msc_lambda_limit":0.1,"msc_range_factor":0.04,"msc_safety_factor":0.6,"msc_step_algorithm":"safety","photoelectric":true,"rayleigh_scattering":true,"relaxation":"all","verbose":true})json", + R"json({"angle_limit_factor":1.0,"annihilation":true,"apply_cuts":false,"brems":"all","compton_scattering":true,"coulomb_scattering":false,"default_cutoff":0.1,"eloss_fluctuation":true,"em_bins_per_decade":7,"form_factor":"exponential","gamma_conversion":true,"gamma_general":false,"integral_approach":true,"ionization":true,"linear_loss_limit":0.01,"lowest_electron_energy":[0.001,"MeV"],"lpm":true,"max_energy":[100000000.0,"MeV"],"min_energy":[0.0001,"MeV"],"msc":"urban","msc_lambda_limit":0.1,"msc_range_factor":0.04,"msc_safety_factor":0.6,"msc_step_algorithm":"safety","msc_theta_limit":3.141592653589793,"photoelectric":true,"rayleigh_scattering":true,"relaxation":"all","verbose":true})json", std::string(out.dump())); } #endif @@ -805,9 +805,14 @@ TEST_F(FourSteelSlabsEmStandard, em_parameters) EXPECT_DOUBLE_EQ(0.01, em_params.linear_loss_limit); EXPECT_DOUBLE_EQ(0.001, em_params.lowest_electron_energy); EXPECT_EQ(true, em_params.auger); + EXPECT_EQ(MscStepLimitAlgorithm::safety, em_params.msc_step_algorithm); EXPECT_DOUBLE_EQ(0.04, em_params.msc_range_factor); EXPECT_DOUBLE_EQ(0.6, em_params.msc_safety_factor); EXPECT_REAL_EQ(0.1, to_cm(em_params.msc_lambda_limit)); + EXPECT_REAL_EQ(constants::pi, em_params.msc_theta_limit); + EXPECT_EQ(false, em_params.apply_cuts); + EXPECT_EQ(1, em_params.screening_factor); + EXPECT_EQ(1, em_params.angle_limit_factor); } //---------------------------------------------------------------------------// diff --git a/test/celeritas/mat/Material.test.cc b/test/celeritas/mat/Material.test.cc index 290fd34b89..cbd72882b5 100644 --- a/test/celeritas/mat/Material.test.cc +++ b/test/celeritas/mat/Material.test.cc @@ -195,17 +195,17 @@ TEST_F(MaterialTest, material_view) { // vacuum MaterialView mat = params->get(MaterialId{1}); - EXPECT_SOFT_EQ(0, mat.number_density()); - EXPECT_SOFT_EQ(0, mat.temperature()); + EXPECT_EQ(0, mat.number_density()); + EXPECT_EQ(0, mat.temperature()); EXPECT_EQ(MatterState::unspecified, mat.matter_state()); - EXPECT_SOFT_EQ(0, mat.zeff()); - EXPECT_SOFT_EQ(0, mat.density()); - EXPECT_SOFT_EQ(0, mat.electron_density()); - EXPECT_SOFT_EQ(std::numeric_limits::infinity(), - mat.radiation_length()); - EXPECT_SOFT_EQ(0, mat.mean_excitation_energy().value()); - EXPECT_SOFT_EQ(-std::numeric_limits::infinity(), - mat.log_mean_excitation_energy().value()); + EXPECT_EQ(0, mat.zeff()); + EXPECT_EQ(0, mat.density()); + EXPECT_EQ(0, mat.electron_density()); + EXPECT_EQ(std::numeric_limits::infinity(), + mat.radiation_length()); + EXPECT_EQ(0, mat.mean_excitation_energy().value()); + EXPECT_EQ(-std::numeric_limits::infinity(), + mat.log_mean_excitation_energy().value()); // Test element view auto els = mat.elements(); From 5082d521db9c6100ac5bb9a2030bb1f4976f5315 Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Thu, 23 May 2024 13:01:13 +0100 Subject: [PATCH 58/59] Add support for using the build directory as an installation (#1249) --- .github/workflows/build-spack.yml | 4 ++++ CMakeLists.txt | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-spack.yml b/.github/workflows/build-spack.yml index 3327772b25..6e9e788a88 100644 --- a/.github/workflows/build-spack.yml +++ b/.github/workflows/build-spack.yml @@ -138,6 +138,10 @@ jobs: fi ctest --parallel $(nproc) --timeout 15 --output-on-failure \ --test-output-size-passed=32768 --test-output-size-failed=1048576 + - name: Check using build directory as install + run: | + . ${SPACK_VIEW}/rc + CELER_INSTALL_DIR=${PWD}/build ./scripts/ci/test-examples.sh - name: Install working-directory: build run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index ab7084451c..7406289146 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,7 +263,7 @@ celeritas_set_default(CMAKE_INSTALL_MESSAGE LAZY) # Output locations for Celeritas products (used by CeleritasUtils.cmake and # install code below) will mirror the installation layout set(CELERITAS_CMAKE_CONFIG_DIRECTORY - "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake") + "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") set(CELERITAS_HEADER_CONFIG_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_INCLUDEDIR}") set(CELERITAS_LIBRARY_OUTPUT_DIRECTORY @@ -558,7 +558,7 @@ endif() # Where to install configured cmake files set(CELERITAS_INSTALL_CMAKECONFIGDIR - "${CMAKE_INSTALL_LIBDIR}/cmake/Celeritas") + "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") # Build list of CMake files to install set(_cmake_files @@ -585,6 +585,11 @@ install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/backport" COMPONENT development ) +# Copy CMake files to support using Celeritas build dir as an install dir +file(COPY ${_cmake_files} + DESTINATION "${CELERITAS_INSTALL_CMAKECONFIGDIR}" +) + # Export all cache variables that start with CELERITAS_ set(CELERITAS_EXPORT_VARIABLES) macro(celeritas_export_var varname) @@ -664,6 +669,12 @@ install(EXPORT celeritas-targets COMPONENT development ) +# Export targets to the build tree +export(EXPORT celeritas-targets + FILE "${CELERITAS_CMAKE_CONFIG_DIRECTORY}/CeleritasTargets.cmake" + NAMESPACE Celeritas:: +) + if(Celeritas_VERSION VERSION_EQUAL "0.0.0") install(CODE " message(WARNING \"The Celeritas version was not detected during configuration. From 08f509f8d7b3d5bca80e176d7881dabbb563041e Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Thu, 23 May 2024 13:30:04 +0100 Subject: [PATCH 59/59] Implement polycone (#1247) * Allow degenerate cylinder as part of cone * Add string view signature for build_intersect_region * Implement polycone * Fix and slightly refactor polycone * Add parens * Address feedback * Address @esseivaju feedback from #1246 --- src/corecel/cont/Array.hh | 4 + src/corecel/grid/VectorUtils.hh | 2 +- src/corecel/math/HashUtils.hh | 2 - src/orange/CMakeLists.txt | 1 + src/orange/g4org/SolidConverter.cc | 57 +--- src/orange/orangeinp/IntersectRegion.cc | 9 +- src/orange/orangeinp/ObjectIO.json.cc | 21 ++ src/orange/orangeinp/ObjectIO.json.hh | 16 +- src/orange/orangeinp/PolySolid.cc | 252 ++++++++++++++ src/orange/orangeinp/PolySolid.hh | 175 ++++++++++ src/orange/orangeinp/Shape.cc | 2 +- src/orange/orangeinp/Solid.cc | 10 +- .../orangeinp/detail/BuildIntersectRegion.hh | 30 +- test/orange/CMakeLists.txt | 1 + test/orange/g4org/SolidConverter.test.cc | 85 +++-- test/orange/orangeinp/IntersectRegion.test.cc | 19 +- test/orange/orangeinp/PolySolid.test.cc | 316 ++++++++++++++++++ 17 files changed, 913 insertions(+), 89 deletions(-) create mode 100644 src/orange/orangeinp/PolySolid.cc create mode 100644 src/orange/orangeinp/PolySolid.hh create mode 100644 test/orange/orangeinp/PolySolid.test.cc diff --git a/src/corecel/cont/Array.hh b/src/corecel/cont/Array.hh index 7393a821cf..7705eef356 100644 --- a/src/corecel/cont/Array.hh +++ b/src/corecel/cont/Array.hh @@ -147,6 +147,8 @@ operator!=(Array const& lhs, Array const& rhs) //---------------------------------------------------------------------------// } // namespace celeritas +//---------------------------------------------------------------------------// +//! \cond namespace std { //---------------------------------------------------------------------------// @@ -161,8 +163,10 @@ struct tuple_size> template struct tuple_element> { + static_assert(I < std::tuple_size>::value); using type = T; }; //---------------------------------------------------------------------------// } // namespace std +//! \endcond diff --git a/src/corecel/grid/VectorUtils.hh b/src/corecel/grid/VectorUtils.hh index 4334e907a0..3daea86ac3 100644 --- a/src/corecel/grid/VectorUtils.hh +++ b/src/corecel/grid/VectorUtils.hh @@ -42,7 +42,7 @@ inline bool is_monotonic_nondecreasing(Span grid) * True if the grid values are monotonically increasing. */ template -bool is_monotonic_increasing(Span grid) +inline bool is_monotonic_increasing(Span grid) { return all_adjacent(grid.begin(), grid.end(), [](T& left, T& right) { return left < right; diff --git a/src/corecel/math/HashUtils.hh b/src/corecel/math/HashUtils.hh index ffd408d92f..d1371fe5e5 100644 --- a/src/corecel/math/HashUtils.hh +++ b/src/corecel/math/HashUtils.hh @@ -71,8 +71,6 @@ std::size_t hash_combine(Args const&... args) //---------------------------------------------------------------------------// } // namespace celeritas -//---------------------------------------------------------------------------// -// HASH SPECIALIZATIONS //---------------------------------------------------------------------------// //! \cond namespace std diff --git a/src/orange/CMakeLists.txt b/src/orange/CMakeLists.txt index f05e29303f..cae72b60fd 100644 --- a/src/orange/CMakeLists.txt +++ b/src/orange/CMakeLists.txt @@ -31,6 +31,7 @@ list(APPEND SOURCES orangeinp/CsgTreeUtils.cc orangeinp/IntersectRegion.cc orangeinp/IntersectSurfaceBuilder.cc + orangeinp/PolySolid.cc orangeinp/ProtoInterface.cc orangeinp/Shape.cc orangeinp/Solid.cc diff --git a/src/orange/g4org/SolidConverter.cc b/src/orange/g4org/SolidConverter.cc index 080b847ec2..a6d46c5206 100644 --- a/src/orange/g4org/SolidConverter.cc +++ b/src/orange/g4org/SolidConverter.cc @@ -53,6 +53,7 @@ #include "corecel/math/SoftEqual.hh" #include "corecel/sys/TypeDemangler.hh" #include "orange/orangeinp/CsgObject.hh" +#include "orange/orangeinp/PolySolid.hh" #include "orange/orangeinp/Shape.hh" #include "orange/orangeinp/Solid.hh" #include "orange/orangeinp/Transformed.hh" @@ -195,6 +196,13 @@ auto make_solid(G4VSolid const& solid, std::move(enclosed)); } +//---------------------------------------------------------------------------// +template +bool any_positive(Container const& c) +{ + return std::any_of(c.begin(), c.end(), [](auto r) { return r > 0; }); +} + //---------------------------------------------------------------------------// } // namespace @@ -312,22 +320,8 @@ auto SolidConverter::cons(arg_type solid_base) -> result_type solid.GetInnerRadiusPlusZ()); auto hh = scale_(solid.GetZHalfLength()); - if (outer_r[0] == outer_r[1]) - { - std::optional inner; - if (inner_r[0] || inner_r[1]) - { - inner = Cylinder{inner_r[0], hh}; - } - - return make_solid(solid, - Cylinder{outer_r[0], hh}, - std::move(inner), - make_wedge_azimuthal(solid)); - } - std::optional inner; - if (inner_r[0] || inner_r[1]) + if (any_positive(inner_r)) { inner = Cone{inner_r, hh}; } @@ -495,31 +489,16 @@ auto SolidConverter::polycone(arg_type solid_base) -> result_type rmax[i] = scale_(params.Rmax[i]); } - if (zs.size() == 2 && rmin[0] == 0 && rmin[1] == 0) + if (!any_positive(rmin)) { - // Special case: displaced cone/cylinder - double const hh = (zs[1] - zs[0]) / 2; - result_type result; - if (rmax[0] == rmax[1]) - { - // Cylinder is a special case - result = make_shape(solid, rmax[0], hh); - } - else - { - result = make_shape(solid, Cone::Real2{rmax[0], rmin[1]}, hh); - } - - double dz = (zs[1] + zs[0]) / 2; - if (dz != 0) - { - result = std::make_shared(std::move(result), - Translation{{0, 0, dz}}); - } - return result; + // No interior shape + rmin.clear(); } - CELER_NOT_IMPLEMENTED("polycone"); + return PolyCone::or_solid( + std::string{solid.GetName()}, + PolySegments{std::move(rmin), std::move(rmax), std::move(zs)}, + make_wedge_azimuthal_poly(solid)); } //---------------------------------------------------------------------------// @@ -562,8 +541,8 @@ auto SolidConverter::polyhedra(arg_type solid_base) -> result_type double dz = (zs[1] + zs[0]) / 2; if (dz != 0) { - result = std::make_shared( - std::move(result), Translation{{0, 0, zs[0] - hh}}); + result = std::make_shared(std::move(result), + Translation{{0, 0, dz}}); } return result; diff --git a/src/orange/orangeinp/IntersectRegion.cc b/src/orange/orangeinp/IntersectRegion.cc index 8376f6770b..4f71074ebc 100644 --- a/src/orange/orangeinp/IntersectRegion.cc +++ b/src/orange/orangeinp/IntersectRegion.cc @@ -105,7 +105,6 @@ Cone::Cone(Real2 const& radii, real_type halfheight) { CELER_VALIDATE(radii_[i] >= 0, << "negative radius: " << radii_[i]); } - CELER_VALIDATE(radii_[0] != radii_[1], << "radii cannot be equal"); CELER_VALIDATE(hh_ > 0, << "nonpositive halfheight: " << hh_); } @@ -134,6 +133,14 @@ bool Cone::encloses(Cone const& other) const */ void Cone::build(IntersectSurfaceBuilder& insert_surface) const { + if (CELER_UNLIKELY( + SoftEqual{insert_surface.tol().rel}(radii_[0], radii_[1]))) + { + // Degenerate cone: build a cylinder instead + Cylinder cyl{real_type{0.5} * (radii_[0] + radii_[1]), hh_}; + return cyl.build(insert_surface); + } + // Build the bottom and top planes insert_surface(Sense::outside, PlaneZ{-hh_}); insert_surface(Sense::inside, PlaneZ{hh_}); diff --git a/src/orange/orangeinp/ObjectIO.json.cc b/src/orange/orangeinp/ObjectIO.json.cc index e9be19c5b5..7edaf01a85 100644 --- a/src/orange/orangeinp/ObjectIO.json.cc +++ b/src/orange/orangeinp/ObjectIO.json.cc @@ -16,6 +16,7 @@ #include "CsgObject.hh" #include "IntersectRegion.hh" #include "ObjectInterface.hh" +#include "PolySolid.hh" #include "Shape.hh" #include "Solid.hh" #include "Transformed.hh" @@ -116,6 +117,17 @@ void to_json(nlohmann::json& j, NegatedObject const& obj) SIO_ATTR_PAIR(obj, daughter)}; } +void to_json(nlohmann::json& j, PolyCone const& obj) +{ + j = {{"_type", "polycone"}, + SIO_ATTR_PAIR(obj, label), + SIO_ATTR_PAIR(obj, segments)}; + if (auto sea = obj.enclosed_angle()) + { + j["enclosed_angle"] = sea; + } +} + void to_json(nlohmann::json& j, ShapeBase const& obj) { j = {{"_type", "shape"}, @@ -150,6 +162,15 @@ void to_json(nlohmann::json& j, Transformed const& obj) //---------------------------------------------------------------------------// //!@{ //! Write helper classes to JSON +void to_json(nlohmann::json& j, PolySegments const& ps) +{ + j = {{SIO_ATTR_PAIR(ps, outer), SIO_ATTR_PAIR(ps, z)}}; + if (ps.has_exclusion()) + { + j.push_back(SIO_ATTR_PAIR(ps, inner)); + } +} + void to_json(nlohmann::json& j, SolidEnclosedAngle const& sea) { j = {{"start", sea.start().value()}, {"interior", sea.interior().value()}}; diff --git a/src/orange/orangeinp/ObjectIO.json.hh b/src/orange/orangeinp/ObjectIO.json.hh index 50ba32173b..5bd1a04e31 100644 --- a/src/orange/orangeinp/ObjectIO.json.hh +++ b/src/orange/orangeinp/ObjectIO.json.hh @@ -21,10 +21,12 @@ class ObjectInterface; template class JoinObjects; class NegatedObject; +class PolyCone; class ShapeBase; class SolidBase; class Transformed; +class PolySegments; class SolidEnclosedAngle; class IntersectRegionInterface; @@ -45,14 +47,16 @@ std::string to_string(ObjectInterface const&); // Write objects to JSON template -void to_json(nlohmann::json& j, JoinObjects const& sb); -void to_json(nlohmann::json& j, NegatedObject const& sb); -void to_json(nlohmann::json& j, ShapeBase const& sb); -void to_json(nlohmann::json& j, SolidBase const& sb); -void to_json(nlohmann::json& j, Transformed const& sb); +void to_json(nlohmann::json& j, JoinObjects const&); +void to_json(nlohmann::json& j, NegatedObject const&); +void to_json(nlohmann::json& j, PolyCone const&); +void to_json(nlohmann::json& j, ShapeBase const&); +void to_json(nlohmann::json& j, SolidBase const&); +void to_json(nlohmann::json& j, Transformed const&); // Write helper classes to JSON -void to_json(nlohmann::json& j, SolidEnclosedAngle const& sea); +void to_json(nlohmann::json& j, PolySegments const&); +void to_json(nlohmann::json& j, SolidEnclosedAngle const&); // Write intersect regions to JSON void to_json(nlohmann::json& j, IntersectRegionInterface const& cr); diff --git a/src/orange/orangeinp/PolySolid.cc b/src/orange/orangeinp/PolySolid.cc new file mode 100644 index 0000000000..6817099b06 --- /dev/null +++ b/src/orange/orangeinp/PolySolid.cc @@ -0,0 +1,252 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/orangeinp/PolySolid.cc +//---------------------------------------------------------------------------// +#include "PolySolid.hh" + +#include "corecel/cont/Range.hh" +#include "corecel/grid/VectorUtils.hh" +#include "corecel/io/JsonPimpl.hh" +#include "corecel/math/SoftEqual.hh" +#include "orange/transform/Translation.hh" + +#include "Transformed.hh" + +#include "detail/BuildIntersectRegion.hh" +#include "detail/VolumeBuilder.hh" + +#if CELERITAS_USE_JSON +# include "ObjectIO.json.hh" +#endif + +namespace celeritas +{ +namespace orangeinp +{ +namespace +{ +//---------------------------------------------------------------------------// +//! Construct the unioned "interior" of a polysolid +template +[[nodiscard]] NodeId construct_segments(PolySolidBase const& base, + T&& build_region, + detail::VolumeBuilder& vb) +{ + std::string const label{base.label()}; + auto const& segments = base.segments(); + CELER_ASSERT(segments.z().size() == segments.size() + 1); + + SoftEqual soft_eq{vb.tol().rel}; + std::vector segment_nodes; + + for (auto i : range(segments.size())) + { + // Translate this segment along z + auto const [zlo, zhi] = segments.z(i); + if (soft_eq(zlo, zhi)) + { + // Effectively zero height segment (degenerate: perhaps stacked + // cylinders, for example) + continue; + } + real_type const hz = (zhi - zlo) / 2; + auto scoped_transform + = vb.make_scoped_transform(Translation{{0, 0, zlo + hz}}); + + // Build outer shape + NodeId segment_node; + { + auto outer = build_region(segments.outer(i), hz); + segment_node = build_intersect_region( + vb, std::string{label}, std::to_string(i) + ".interior", outer); + } + + if (segments.has_exclusion()) + { + // Build inner shape + auto inner = build_region(segments.inner(i), hz); + NodeId inner_node = build_intersect_region( + vb, std::string{label}, std::to_string(i) + ".excluded", inner); + + // Subtract (i.e., "and not") inner shape from this segment + auto sub_node = vb.insert_region({}, Negated{inner_node}); + segment_node + = vb.insert_region(Label{label, std::to_string(i)}, + Joined{op_and, {segment_node, sub_node}}); + } + segment_nodes.push_back(segment_node); + } + + // Union the given segments to create a new CSG node + return vb.insert_region(Label{label, "segments"}, + Joined{op_or, std::move(segment_nodes)}); +} + +//---------------------------------------------------------------------------// +/*! + * Construct an enclosed angle if applicable. + */ +[[nodiscard]] NodeId construct_enclosed_angle(PolySolidBase const& base, + detail::VolumeBuilder& vb, + NodeId result) +{ + if (auto const& sea = base.enclosed_angle()) + { + // The enclosed angle is "true" (specified by the user to truncate the + // shape azimuthally): construct a wedge to be added or deleted + auto&& [sense, wedge] = sea.make_wedge(); + NodeId wedge_id + = build_intersect_region(vb, base.label(), "angle", wedge); + if (sense == Sense::outside) + { + wedge_id = vb.insert_region({}, Negated{wedge_id}); + } + result + = vb.insert_region(Label{std::string{base.label()}, "restricted"}, + Joined{op_and, {result, wedge_id}}); + } + return result; +} + +//---------------------------------------------------------------------------// +} // namespace + +//---------------------------------------------------------------------------// +/*! + * Construct from a filled polygon solid. + */ +PolySegments::PolySegments(VecReal&& outer, VecReal&& z) + : PolySegments{{}, std::move(outer), std::move(z)} +{ +} + +//---------------------------------------------------------------------------// +/*! + * Construct from a shell of a polygon solid. + */ +PolySegments::PolySegments(VecReal&& inner, VecReal&& outer, VecReal&& z) + : inner_{std::move(inner)}, outer_{std::move(outer)}, z_{std::move(z)} +{ + CELER_VALIDATE(z_.size() >= 2, + << "no axial segments was specified: at least 2 points " + "needed (given " + << z_.size() << ")"); + CELER_VALIDATE(outer_.size() == z_.size(), + << "inconsistent outer radius size (" << outer_.size() + << "): expected " << z_.size()); + CELER_VALIDATE(inner_.empty() || inner_.size() == z_.size(), + << "inconsistent inner radius size (" << inner_.size() + << "): expected " << z_.size()); + + CELER_VALIDATE(is_monotonic_nondecreasing(make_span(z_)), + << "axial grid has increasing grid points"); + for (auto i : range(outer_.size())) + { + CELER_VALIDATE(outer_[i] >= 0, << "invalid outer radius " << outer_[i]); + CELER_VALIDATE(inner_.empty() + || (inner_[i] >= 0 && inner_[i] <= outer_[i]), + << "invalid inner radius " << inner_[i]); + } +} + +//---------------------------------------------------------------------------// +/*! + * Build with label, axial segments, optional restriction. + */ +PolySolidBase::PolySolidBase(std::string&& label, + PolySegments&& segments, + SolidEnclosedAngle&& enclosed) + : label_{std::move(label)} + , segments_{std::move(segments)} + , enclosed_{std::move(enclosed)} +{ +} + +//---------------------------------------------------------------------------// +/*! + * Return a polycone *or* a simplified version for only a single segment. + */ +auto PolyCone::or_solid(std::string&& label, + PolySegments&& segments, + SolidEnclosedAngle&& enclosed) -> SPConstObject +{ + if (segments.size() > 1) + { + // Can't be simplified: make a polycone + return std::make_shared( + std::move(label), std::move(segments), std::move(enclosed)); + } + + auto const [zlo, zhi] = segments.z(0); + real_type const hh = (zhi - zlo) / 2; + + Cone outer{segments.outer(0), hh}; + std::optional inner; + if (segments.has_exclusion()) + { + inner = Cone{segments.inner(0), hh}; + } + + auto result = ConeSolid::or_shape(std::move(label), + std::move(outer), + std::move(inner), + std::move(enclosed)); + if (real_type dz = (zhi + zlo) / 2; dz != 0) + { + result = std::make_shared(std::move(result), + Translation{{0, 0, dz}}); + } + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Build with label, axial segments, optional restriction. + */ +PolyCone::PolyCone(std::string&& label, + PolySegments&& segments, + SolidEnclosedAngle&& enclosed) + : PolySolidBase{std::move(label), std::move(segments), std::move(enclosed)} +{ +} + +//---------------------------------------------------------------------------// +/*! + * Construct a volume from this shape. + */ +NodeId PolyCone::build(VolumeBuilder& vb) const +{ + using Real2 = PolySegments::Real2; + auto build_cone = [](Real2 const& radii, real_type hh) { + return Cone{radii, hh}; + }; + + // Construct union of all cone segments + NodeId result = construct_segments(*this, build_cone, vb); + + // TODO: after adding short-circuit logic to evaluator, add "acceleration" + // structures here, e.g. "inside(inner cylinder) || [inside(outer cylinder) + // && (original union)]" + + // Construct azimuthal truncation if applicable + result = construct_enclosed_angle(*this, vb, result); + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Write the shape to JSON. + */ +void PolyCone::output(JsonPimpl* j) const +{ + to_json_pimpl(j, *this); +} + +//---------------------------------------------------------------------------// +} // namespace orangeinp +} // namespace celeritas diff --git a/src/orange/orangeinp/PolySolid.hh b/src/orange/orangeinp/PolySolid.hh new file mode 100644 index 0000000000..ff3837cc7c --- /dev/null +++ b/src/orange/orangeinp/PolySolid.hh @@ -0,0 +1,175 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/orangeinp/PolySolid.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/OpaqueId.hh" + +#include "IntersectRegion.hh" +#include "ObjectInterface.hh" +#include "Solid.hh" + +namespace celeritas +{ +namespace orangeinp +{ +//---------------------------------------------------------------------------// +/*! + * Radial extents and axial segments for a stacked solid. + * + * Axial grid points must be nondecreasing. If "inner" points are specified, + * they must be less than the outer points and more than zero. The inner list + * is allowed to be empty indictating no inner (hollow) exclusion. + */ +class PolySegments +{ + public: + //!@{ + //! \name Type aliases + using VecReal = std::vector; + using Real2 = celeritas::Array; + //!@} + + public: + // Construct from a filled polygon solid + PolySegments(VecReal&& outer, VecReal&& z); + + // Construct from a shell of a polygon solid + PolySegments(VecReal&& inner, VecReal&& outer, VecReal&& z); + + //! Number of segments (one less than grid points) + size_type size() const { return outer_.size() - 1; } + + // Access the inner radii (for building 'exclusion' shape) + inline VecReal const& inner() const; + + //! Access the outer radii (for building 'interior' shape) + VecReal const& outer() const { return outer_; } + + //! Access the z planes + VecReal const& z() const { return z_; } + + // Access lo/hi inner/exclusion radii for a segment + inline Real2 inner(size_type) const; + + // Access lo/hi outer radii for a segment + inline Real2 outer(size_type) const; + + // Access lo/hi z values for a segment + inline Real2 z(size_type) const; + + //! Whether there is an internal subtraction from the poly + bool has_exclusion() const { return !inner_.empty(); } + + private: + VecReal inner_; + VecReal outer_; + VecReal z_; +}; + +//---------------------------------------------------------------------------// +/*! + * Access the inner radii (for building 'exclusion' shape). + */ +auto PolySegments::inner() const -> VecReal const& +{ + CELER_EXPECT(has_exclusion()); + return inner_; +} + +//---------------------------------------------------------------------------// +/*! + * Access lo/hi inner/exclusion radii for a segment. + */ +auto PolySegments::inner(size_type i) const -> Real2 +{ + CELER_EXPECT(this->has_exclusion() && i < this->size()); + return {inner_[i], inner_[i + 1]}; +} + +//---------------------------------------------------------------------------// +/*! + * Access lo/hi outer radii for a segment. + */ +auto PolySegments::outer(size_type i) const -> Real2 +{ + CELER_EXPECT(i < this->size()); + return {outer_[i], outer_[i + 1]}; +} + +//---------------------------------------------------------------------------// +/*! + * Access lo/hi z values for a segment. + */ +auto PolySegments::z(size_type i) const -> Real2 +{ + CELER_EXPECT(i < this->size()); + return {z_[i], z_[i + 1]}; +} + +//---------------------------------------------------------------------------// +/*! + * A segmented stack of same-type shapes with an azimuthal truncation. + */ +class PolySolidBase : public ObjectInterface +{ + public: + //! Get the user-provided label + std::string_view label() const final { return label_; } + + //! Axial segments + PolySegments const& segments() const { return segments_; } + + //! Optional azimuthal angular restriction + SolidEnclosedAngle enclosed_angle() const { return enclosed_; } + + protected: + PolySolidBase(std::string&& label, + PolySegments&& segments, + SolidEnclosedAngle&& enclosed); + + //!@{ + //! Allow construction and assignment only through daughter classes + virtual ~PolySolidBase() = default; + CELER_DEFAULT_COPY_MOVE(PolySolidBase); + //!@} + + private: + std::string label_; + PolySegments segments_; + SolidEnclosedAngle enclosed_; +}; + +//---------------------------------------------------------------------------// +/*! + * A series of stacked cones or cylinders or combination of both. + */ +class PolyCone final : public PolySolidBase +{ + public: + // Return a polycone *or* a simplified version for only a single segment + static SPConstObject or_solid(std::string&& label, + PolySegments&& segments, + SolidEnclosedAngle&& enclosed); + + // Build with label, axial segments, optional restriction + PolyCone(std::string&& label, + PolySegments&& segments, + SolidEnclosedAngle&& enclosed); + + // Construct a volume from this object + NodeId build(VolumeBuilder&) const final; + + // Write the shape to JSON + void output(JsonPimpl*) const final; +}; + +//---------------------------------------------------------------------------// +} // namespace orangeinp +} // namespace celeritas diff --git a/src/orange/orangeinp/Shape.cc b/src/orange/orangeinp/Shape.cc index 66c58420a8..8eb78697a7 100644 --- a/src/orange/orangeinp/Shape.cc +++ b/src/orange/orangeinp/Shape.cc @@ -26,7 +26,7 @@ namespace orangeinp NodeId ShapeBase::build(VolumeBuilder& vb) const { return detail::build_intersect_region( - vb, std::string{this->label()}, {}, this->interior()); + vb, this->label(), {}, this->interior()); } //---------------------------------------------------------------------------// diff --git a/src/orange/orangeinp/Solid.cc b/src/orange/orangeinp/Solid.cc index 475ea193e8..5a4ad090da 100644 --- a/src/orange/orangeinp/Solid.cc +++ b/src/orange/orangeinp/Solid.cc @@ -72,14 +72,14 @@ NodeId SolidBase::build(VolumeBuilder& vb) const // Build the outside-of-the-shell node nodes.push_back(build_intersect_region( - vb, std::string{this->label()}, "interior", this->interior())); + vb, this->label(), "interior", this->interior())); if (auto* exclu = this->excluded()) { // Construct the excluded region by building a convex solid, then // negating it - NodeId smaller = build_intersect_region( - vb, std::string{this->label()}, "excluded", *exclu); + NodeId smaller + = build_intersect_region(vb, this->label(), "excluded", *exclu); nodes.push_back(vb.insert_region({}, Negated{smaller})); } @@ -88,8 +88,8 @@ NodeId SolidBase::build(VolumeBuilder& vb) const // The enclosed angle is "true" (specified by the user to truncate the // shape azimuthally): construct a wedge to be added or deleted auto&& [sense, wedge] = sea.make_wedge(); - NodeId wedge_id = build_intersect_region( - vb, std::string{this->label()}, "angle", wedge); + NodeId wedge_id + = build_intersect_region(vb, this->label(), "angle", wedge); if (sense == Sense::outside) { wedge_id = vb.insert_region({}, Negated{wedge_id}); diff --git a/src/orange/orangeinp/detail/BuildIntersectRegion.hh b/src/orange/orangeinp/detail/BuildIntersectRegion.hh index f2562a59b6..b7c9b46eed 100644 --- a/src/orange/orangeinp/detail/BuildIntersectRegion.hh +++ b/src/orange/orangeinp/detail/BuildIntersectRegion.hh @@ -8,6 +8,7 @@ #pragma once #include +#include #include "orange/orangeinp/CsgTypes.hh" @@ -22,19 +23,32 @@ class VolumeBuilder; //---------------------------------------------------------------------------// // Build a intersect region -NodeId build_intersect_region(VolumeBuilder& vb, - std::string&& label, - std::string&& face_prefix, - IntersectRegionInterface const& region); +[[nodiscard]] NodeId +build_intersect_region(VolumeBuilder& vb, + std::string&& label, + std::string&& face_prefix, + IntersectRegionInterface const& region); -//! Build an intersect region with no face prefix -inline NodeId build_intersect_region(VolumeBuilder& vb, - std::string&& label, - IntersectRegionInterface const& region) +//! Build a intersect region with no face prefix +[[nodiscard]] inline NodeId +build_intersect_region(VolumeBuilder& vb, + std::string&& label, + IntersectRegionInterface const& region) { return build_intersect_region(vb, std::move(label), {}, region); } +//! Build a intersect region using a string view +[[nodiscard]] inline NodeId +build_intersect_region(VolumeBuilder& vb, + std::string_view label, + std::string&& face_prefix, + IntersectRegionInterface const& region) +{ + return build_intersect_region( + vb, std::string{label}, std::move(face_prefix), region); +} + //---------------------------------------------------------------------------// } // namespace detail } // namespace orangeinp diff --git a/test/orange/CMakeLists.txt b/test/orange/CMakeLists.txt index adb62b232c..969223aca5 100644 --- a/test/orange/CMakeLists.txt +++ b/test/orange/CMakeLists.txt @@ -77,6 +77,7 @@ celeritas_add_test(orangeinp/CsgTree.test.cc) celeritas_add_test(orangeinp/CsgTreeUtils.test.cc) celeritas_add_test(orangeinp/IntersectRegion.test.cc) celeritas_add_test(orangeinp/IntersectSurfaceBuilder.test.cc) +celeritas_add_test(orangeinp/PolySolid.test.cc) celeritas_add_test(orangeinp/Shape.test.cc) celeritas_add_test(orangeinp/Solid.test.cc) celeritas_add_test(orangeinp/Transformed.test.cc) diff --git a/test/orange/g4org/SolidConverter.test.cc b/test/orange/g4org/SolidConverter.test.cc index c2944e46e6..9b31374c9e 100644 --- a/test/orange/g4org/SolidConverter.test.cc +++ b/test/orange/g4org/SolidConverter.test.cc @@ -156,7 +156,7 @@ TEST_F(SolidConverterTest, cons) { this->build_and_test( G4Cons("Solid TubeLike #1", 0, 50, 0, 50, 50, 0, 360), - R"json({"_type":"shape","interior":{"_type":"cylinder","halfheight":5.0,"radius":5.0},"label":"Solid TubeLike #1"})json", + R"json({"_type":"shape","interior":{"_type":"cone","halfheight":5.0,"radii":[5.0,5.0]},"label":"Solid TubeLike #1"})json", {{0, 0, 4}, {0, 0, 5}, {0, 0, 6}, {4, 0, 0}, {5, 0, 0}, {6, 0, 0}}); this->build_and_test( @@ -318,34 +318,69 @@ TEST_F(SolidConverterTest, para) TEST_F(SolidConverterTest, polycone) { - static double const z[] = {6, 630}; - static double const rmin[] = {0, 0}; - static double const rmax[] = {95, 95}; - this->build_and_test( - G4Polycone("HGCalEE", 0, 360 * deg, std::size(z), z, rmin, rmax), - R"json({"_type":"transformed","daughter":{"_type":"shape","interior":{"_type":"cylinder","halfheight":31.2,"radius":9.5},"label":"HGCalEE"},"transform":{"_type":"translation","data":[0.0,0.0,31.8]}})json", - {{-6.72, -6.72, 0.7}, - {6.72, 6.72, 62.9}, - {0, 0, 31.8}, - {-9.5, -9.5, 0.5}, - {-6.72, 9.0, 0.70}}); + { + static double const z[] = {6, 630}; + static double const rmin[] = {0, 0}; + static double const rmax[] = {95, 95}; + this->build_and_test( + G4Polycone("HGCalEE", 0, 360 * deg, std::size(z), z, rmin, rmax), + R"json({"_type":"transformed","daughter":{"_type":"shape","interior":{"_type":"cone","halfheight":31.2,"radii":[9.5,9.5]},"label":"HGCalEE"},"transform":{"_type":"translation","data":[0.0,0.0,31.8]}})json", + {{-6.72, -6.72, 0.7}, + {6.72, 6.72, 62.9}, + {0, 0, 31.8}, + {-9.5, -9.5, 0.5}, + {-6.72, 9.0, 0.70}}); + } + { + static double const z[] = {0, 5, 20, 20, 63.3, 115.2, 144}; + static double const rmin[] = {1954, 1954, 1954, 2016, 2016, 2044, 2044}; + static double const rmax[] = {2065, 2070, 2070, 2070, 2070, 2070, 2070}; + + this->build_and_test( + G4Polycone( + "EMEC_FrontOuterRing", 0, 360 * deg, std::size(z), z, rmin, rmax), + R"json({"_type":"polycone","label":"EMEC_FrontOuterRing","segments":[{"outer":[206.5,207.0,207.0,207.0,207.0,207.0,207.0],"z":[0.0,0.5,2.0,2.0,6.33,11.52,14.4]},["inner",[195.4,195.4,195.4,201.6,201.6,204.4,204.4]]]})json", + {{0, 0, -0.1}, + {195.3, 0, 4.999}, + {195.5, 0, 4.999}, + {206.9, 0, 0.25}, + {204.5, 0, 14.3}}); + } + { + static double const z[] = {-165, -10, -10, 10, 10, 165}; + static double const rmin[] = {2044, 2044, 2050.5, 2050.5, 2044, 2044}; + static double const rmax[] = {2070, 2070, 2070, 2070, 2070, 2070}; + + this->build_and_test( + G4Polycone("EMEC_WideStretchers", + -5.625 * deg, + 11.25 * deg, + std::size(z), + z, + rmin, + rmax), + R"json({"_type":"polycone","enclosed_angle":{"interior":0.03125,"start":0.984375},"label":"EMEC_WideStretchers","segments":[{"outer":[207.0,207.0,207.0,207.0,207.0,207.0],"z":[-16.5,-1.0,-1.0,1.0,1.0,16.5]},["inner",[204.4,204.4,205.05,205.05,204.4,204.4]]]})json", + {{206, 0, 0}, {-206, 0, 0}}); + } } TEST_F(SolidConverterTest, polyhedra) { - static double const z[] = {-0.6, 0.6}; - static double const rmin[] = {0, 0}; - static double const rmax[] = {61.85, 61.85}; - // flat-top hexagon - this->build_and_test( - G4Polyhedra( - "HGCalEEAbs", 330 * deg, 360 * deg, 6, std::size(z), z, rmin, rmax), - R"json({"_type":"shape","interior":{"_type":"prism","apothem":6.1850000000000005,"halfheight":0.06,"num_sides":6,"orientation":0.5},"label":"HGCalEEAbs"})json", - {{6.18, 6.18, 0.05}, - {0, 0, 0.06}, - {7.15, 7.15, 0.05}, - {3.0, 6.01, 0}, - {6.18, 7.15, 0}}); + { + static double const z[] = {-0.6, 0.6}; + static double const rmin[] = {0, 0}; + static double const rmax[] = {61.85, 61.85}; + // flat-top hexagon + this->build_and_test( + G4Polyhedra( + "HGCalEEAbs", 330 * deg, 360 * deg, 6, std::size(z), z, rmin, rmax), + R"json({"_type":"shape","interior":{"_type":"prism","apothem":6.1850000000000005,"halfheight":0.06,"num_sides":6,"orientation":0.5},"label":"HGCalEEAbs"})json", + {{6.18, 6.18, 0.05}, + {0, 0, 0.06}, + {7.15, 7.15, 0.05}, + {3.0, 6.01, 0}, + {6.18, 7.15, 0}}); + } } TEST_F(SolidConverterTest, sphere) diff --git a/test/orange/orangeinp/IntersectRegion.test.cc b/test/orange/orangeinp/IntersectRegion.test.cc index 6bc96cfc26..867267bc0b 100644 --- a/test/orange/orangeinp/IntersectRegion.test.cc +++ b/test/orange/orangeinp/IntersectRegion.test.cc @@ -184,7 +184,6 @@ using ConeTest = IntersectRegionTest; TEST_F(ConeTest, errors) { - EXPECT_THROW(Cone({1.0, 1.0}, 0.5), RuntimeError); EXPECT_THROW(Cone({-1, 1}, 1), RuntimeError); EXPECT_THROW(Cone({0.5, 1}, 0), RuntimeError); } @@ -235,6 +234,24 @@ TEST_F(ConeTest, downward) EXPECT_VEC_SOFT_EQ((Real3{1.2, 1.2, 0.65}), result.exterior.upper()); } +TEST_F(ConeTest, cylinder) +{ + auto result = this->test(Cone({1.2, 1.2}, 1.3 / 2)); + + static char const expected_node[] = "all(+0, -1, -2)"; + static char const* const expected_surfaces[] + = {"Plane: z=-0.65", "Plane: z=0.65", "Cyl z: r=1.2"}; + + EXPECT_EQ(expected_node, result.node); + EXPECT_VEC_EQ(expected_surfaces, result.surfaces); + EXPECT_VEC_SOFT_EQ((Real3{-0.84852813742386, -0.84852813742386, -0.65}), + result.interior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{0.84852813742386, 0.84852813742386, 0.65}), + result.interior.upper()); + EXPECT_VEC_SOFT_EQ((Real3{-1.2, -1.2, -0.65}), result.exterior.lower()); + EXPECT_VEC_SOFT_EQ((Real3{1.2, 1.2, 0.65}), result.exterior.upper()); +} + TEST_F(ConeTest, truncated) { auto result = this->test(Cone({0.5, 1.5}, 0.5)); diff --git a/test/orange/orangeinp/PolySolid.test.cc b/test/orange/orangeinp/PolySolid.test.cc new file mode 100644 index 0000000000..1b6f8f640a --- /dev/null +++ b/test/orange/orangeinp/PolySolid.test.cc @@ -0,0 +1,316 @@ +//----------------------------------*-C++-*----------------------------------// +// Copyright 2024 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/orangeinp/PolySolid.test.cc +//---------------------------------------------------------------------------// +#include "orange/orangeinp/PolySolid.hh" + +#include "orange/orangeinp/Shape.hh" +#include "orange/orangeinp/Solid.hh" +#include "orange/orangeinp/Transformed.hh" + +#include "CsgTestUtils.hh" +#include "ObjectTestBase.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace orangeinp +{ +namespace test +{ +using Real2 = PolySegments::Real2; + +//---------------------------------------------------------------------------// +TEST(PolySegmentsTest, errors) +{ + // Not enough elements + EXPECT_THROW(PolySegments({}, {}), RuntimeError); + EXPECT_THROW(PolySegments({1}, {2}), RuntimeError); + // Inconsistent sizes + EXPECT_THROW(PolySegments({1}, {2, 2}), RuntimeError); + // Out of order Z + EXPECT_THROW(PolySegments({1, 2, 3}, {2, 1, 3}), RuntimeError); + // Invalid inner size + EXPECT_THROW(PolySegments({1, 2}, {2, 2}, {3, 4, 5}), RuntimeError); + // Inner outside outer + EXPECT_THROW(PolySegments({3, 3}, {2, 3}, {0, 1}), RuntimeError); +} + +TEST(PolySegmentsTest, filled) +{ + PolySegments seg({2, 1, 3, 4}, {-1, 0, 2, 6}); + EXPECT_EQ(3, seg.size()); + EXPECT_FALSE(seg.has_exclusion()); + EXPECT_VEC_EQ((Real2{2, 1}), seg.outer(0)); + EXPECT_VEC_EQ((Real2{1, 3}), seg.outer(1)); + EXPECT_VEC_EQ((Real2{3, 4}), seg.outer(2)); + EXPECT_VEC_EQ((Real2{-1, 0}), seg.z(0)); + EXPECT_VEC_EQ((Real2{2, 6}), seg.z(2)); +} + +TEST(PolySegmentsTest, hollow) +{ + PolySegments seg({1, 0.5, 2.5, 2}, {2, 1, 3, 4}, {-1, 0, 2, 6}); + EXPECT_EQ(3, seg.size()); + EXPECT_TRUE(seg.has_exclusion()); + EXPECT_VEC_EQ((Real2{1, 0.5}), seg.inner(0)); + EXPECT_VEC_EQ((Real2{2.5, 2}), seg.inner(2)); + EXPECT_VEC_EQ((Real2{2, 1}), seg.outer(0)); + EXPECT_VEC_EQ((Real2{3, 4}), seg.outer(2)); + EXPECT_VEC_EQ((Real2{-1, 0}), seg.z(0)); + EXPECT_VEC_EQ((Real2{2, 6}), seg.z(2)); +} + +//---------------------------------------------------------------------------// +class PolyconeTest : public ObjectTestBase +{ + protected: + Tol tolerance() const override { return Tol::from_relative(1e-4); } +}; + +TEST_F(PolyconeTest, filled) +{ + this->build_volume( + PolyCone{"pc", PolySegments{{2, 1, 1, 3}, {-2, -1, 0, 2}}, {}}); + + static char const* const expected_surface_strings[] = { + "Plane: z=-2", + "Plane: z=-1", + "Cone z: t=1 at {0,0,0}", + "Plane: z=0", + "Cyl z: r=1", + "Plane: z=2", + "Cone z: t=1 at {0,0,-1}", + }; + static char const* const expected_volume_strings[] = { + "any(all(+0, -1, -2), all(+1, -3, -4), all(+3, -5, -6))", + }; + static char const* const expected_md_strings[] = { + "", + "", + "pc@0.interior.mz", + "pc@0.interior.pz,pc@1.interior.mz", + "", + "pc@0.interior.kz", + "", + "pc", + "pc@1.interior.pz,pc@2.interior.mz", + "", + "pc@1.interior.cz", + "", + "pc", + "pc@2.interior.pz", + "", + "pc@2.interior.kz", + "", + "pc", + "pc@segments", + }; + + auto const& u = this->unit(); + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); +} + +TEST_F(PolyconeTest, hollow) +{ + this->build_volume(PolyCone{ + "pc", + PolySegments{{0.5, 0.5, 0.75, 1}, {2, 1, 1, 3}, {-2, -1, 0, 2}}, + {}}); + + static char const* const expected_surface_strings[] = { + "Plane: z=-2", + "Plane: z=-1", + "Cone z: t=1 at {0,0,0}", + "Cyl z: r=0.5", + "Plane: z=0", + "Cyl z: r=1", + "Cone z: t=0.25 at {0,0,-3}", + "Plane: z=2", + "Cone z: t=1 at {0,0,-1}", + "Cone z: t=0.125 at {0,0,-6}", + }; + static char const* const expected_volume_strings[] = { + "any(all(all(+0, -1, -2), !all(+0, -1, -3)), all(all(+1, -4, -5), " + "!all(+1, -4, -6)), all(all(+4, -7, -8), !all(+4, -7, -9)))", + }; + static char const* const expected_md_strings[] = { + "", + "", + "pc@0.excluded.mz,pc@0.interior.mz", + "pc@0.excluded.pz,pc@0.interior.pz,pc@1.excluded.mz,pc@1.interior.mz", + "", + "pc@0.interior.kz", + "", + "pc", + "pc@0.excluded.cz", + "", + "pc", + "", + "pc@0", + "pc@1.excluded.pz,pc@1.interior.pz,pc@2.excluded.mz,pc@2.interior.mz", + "", + "pc@1.interior.cz", + "", + "pc", + "pc@1.excluded.kz", + "", + "pc", + "", + "pc@1", + "pc@2.excluded.pz,pc@2.interior.pz", + "", + "pc@2.interior.kz", + "", + "pc", + "pc@2.excluded.kz", + "", + "pc", + "", + "pc@2", + "pc@segments", + }; + + auto const& u = this->unit(); + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); +} + +TEST_F(PolyconeTest, sliced) +{ + this->build_volume(PolyCone{"pc", + PolySegments{{2, 1, 3}, {-2, 0, 2}}, + SolidEnclosedAngle{Turn{0.125}, Turn{0.75}}}); + + static char const* const expected_surface_strings[] = { + "Plane: z=-2", + "Plane: z=0", + "Cone z: t=0.5 at {0,0,2}", + "Plane: z=2", + "Cone z: t=1 at {0,0,-1}", + "Plane: n={0.70711,0.70711,0}, d=0", + "Plane: n={0.70711,-0.70711,0}, d=0", + }; + static char const* const expected_volume_strings[] = { + "all(any(all(+0, -1, -2), all(+1, -3, -4)), !all(+5, +6))", + }; + static char const* const expected_md_strings[] = { + "", + "", + "pc@0.interior.mz", + "pc@0.interior.pz,pc@1.interior.mz", + "", + "pc@0.interior.kz", + "", + "pc", + "pc@1.interior.pz", + "", + "pc@1.interior.kz", + "", + "pc", + "pc@segments", + "pc@angle.p0", + "pc@angle.p1", + "pc", + "", + "pc@restricted", + }; + + auto const& u = this->unit(); + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); +} + +TEST_F(PolyconeTest, degenerate) +{ + this->build_volume( + PolyCone{"cyls", PolySegments{{2, 2, 1, 1}, {-2, -1, -1, 2}}, {}}); + static char const* const expected_surface_strings[] = { + "Plane: z=-2", + "Plane: z=-1", + "Cyl z: r=2", + "Plane: z=2", + "Cyl z: r=1", + }; + static char const* const expected_volume_strings[] = { + "any(all(+0, -1, -2), all(+1, -3, -4))", + }; + static char const* const expected_md_strings[] = { + "", + "", + "cyls@0.interior.mz", + "cyls@0.interior.pz,cyls@2.interior.mz", + "", + "cyls@0.interior.cz", + "", + "cyls", + "cyls@2.interior.pz", + "", + "cyls@2.interior.cz", + "", + "cyls", + "cyls@segments", + }; + + auto const& u = this->unit(); + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); + EXPECT_VEC_EQ(expected_md_strings, md_strings(u)); +} + +TEST_F(PolyconeTest, or_solid) +{ + { + auto s = PolyCone::or_solid( + "cone", PolySegments{{1, 2}, {-2, 2}}, SolidEnclosedAngle{}); + EXPECT_TRUE(s); + EXPECT_TRUE(dynamic_cast(s.get())); + this->build_volume(*s); + } + { + auto s = PolyCone::or_solid("hollowcone", + PolySegments{{0.5, 0.75}, {1, 2}, {-2, 2}}, + SolidEnclosedAngle{}); + EXPECT_TRUE(s); + EXPECT_TRUE(dynamic_cast(s.get())); + this->build_volume(*s); + } + { + auto s = PolyCone::or_solid( + "transcyl", PolySegments{{2, 2}, {0, 4}}, SolidEnclosedAngle{}); + EXPECT_TRUE(s); + EXPECT_TRUE(dynamic_cast(s.get())); + this->build_volume(*s); + } + + static char const* const expected_surface_strings[] = { + "Plane: z=-2", + "Plane: z=2", + "Cone z: t=0.25 at {0,0,-6}", + "Cone z: t=0.0625 at {0,0,-10}", + "Plane: z=0", + "Plane: z=4", + "Cyl z: r=2", + }; + static char const* const expected_volume_strings[] = { + "all(+0, -1, -2)", + "all(all(+0, -1, -2), !all(+0, -1, -3))", + "all(+4, -5, -6)", + }; + + auto const& u = this->unit(); + EXPECT_VEC_EQ(expected_surface_strings, surface_strings(u)); + EXPECT_VEC_EQ(expected_volume_strings, volume_strings(u)); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace orangeinp +} // namespace celeritas