From d2864bba8f8329903673de4229bd6b2fc5d6d579 Mon Sep 17 00:00:00 2001 From: Hayden Hollenbeck Date: Mon, 25 Nov 2024 12:41:49 -0500 Subject: [PATCH] Refactored optical mock tests to use GlobalTestBase, and to split validation checks into separate file --- test/celeritas/CMakeLists.txt | 8 +- test/celeritas/optical/Absorption.test.cc | 27 +- .../optical/ImportedModelAdapter.test.cc | 36 +- test/celeritas/optical/MfpBuilder.test.cc | 18 +- test/celeritas/optical/MockImportedData.cc | 319 ------------------ test/celeritas/optical/MockImportedData.hh | 143 -------- test/celeritas/optical/MockValidation.test.cc | 66 ---- test/celeritas/optical/OpticalMockTestBase.cc | 281 +++++++++++++++ test/celeritas/optical/OpticalMockTestBase.hh | 77 +++++ test/celeritas/optical/Rayleigh.test.cc | 28 +- .../optical/RayleighMfpCalculator.test.cc | 92 ++--- test/celeritas/optical/ValidationUtils.cc | 111 ++++++ test/celeritas/optical/ValidationUtils.hh | 184 ++++++++++ 13 files changed, 750 insertions(+), 640 deletions(-) delete mode 100644 test/celeritas/optical/MockImportedData.cc delete mode 100644 test/celeritas/optical/MockImportedData.hh delete mode 100644 test/celeritas/optical/MockValidation.test.cc create mode 100644 test/celeritas/optical/OpticalMockTestBase.cc create mode 100644 test/celeritas/optical/OpticalMockTestBase.hh create mode 100644 test/celeritas/optical/ValidationUtils.cc create mode 100644 test/celeritas/optical/ValidationUtils.hh diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index 006d1fb5f2..c9ad6b0aac 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -79,9 +79,10 @@ celeritas_add_test_library(testcel_celeritas grid/CalculatorTestBase.cc io/EventIOTestBase.cc neutron/NeutronTestBase.cc - optical/MockImportedData.cc + optical/OpticalMockTestBase.cc optical/OpticalTestBase.cc optical/InteractorHostTestBase.cc + optical/ValidationUtils.cc phys/InteractionIO.cc phys/InteractorHostTestBase.cc phys/MockModel.cc @@ -308,12 +309,11 @@ celeritas_add_test(optical/Cerenkov.test.cc) celeritas_add_test(optical/OpticalCollector.test.cc ${_needs_geant4}) celeritas_add_test(optical/OpticalUtils.test.cc) celeritas_add_test(optical/Scintillation.test.cc) -celeritas_add_test(optical/Rayleigh.test.cc ${_needs_double}) -celeritas_add_test(optical/Absorption.test.cc ${_needs_double}) +celeritas_add_test(optical/Rayleigh.test.cc) +celeritas_add_test(optical/Absorption.test.cc) celeritas_add_test(optical/ImportedModelAdapter.test.cc) celeritas_add_test(optical/MfpBuilder.test.cc) celeritas_add_test(optical/RayleighMfpCalculator.test.cc) -celeritas_add_test(optical/MockValidation.test.cc) #-----------------------------------------------------------------------------# # Mat diff --git a/test/celeritas/optical/Absorption.test.cc b/test/celeritas/optical/Absorption.test.cc index 491935add3..6a4cbe2c84 100644 --- a/test/celeritas/optical/Absorption.test.cc +++ b/test/celeritas/optical/Absorption.test.cc @@ -8,7 +8,8 @@ #include "celeritas/optical/interactor/AbsorptionInteractor.hh" #include "celeritas/optical/model/AbsorptionModel.hh" -#include "MockImportedData.hh" +#include "OpticalMockTestBase.hh" +#include "ValidationUtils.hh" #include "celeritas_test.hh" namespace celeritas @@ -28,7 +29,7 @@ class AbsorptionInteractorTest : public ::celeritas::test::Test void SetUp() override {} }; -class AbsorptionModelTest : public MockImportedData +class AbsorptionModelTest : public OpticalMockTestBase { protected: void SetUp() override {} @@ -36,15 +37,10 @@ class AbsorptionModelTest : public MockImportedData //! Construct absorption model from mock data std::shared_ptr create_model() { - auto models = MockImportedData::create_imported_models(); - - import_model_id_ - = models->builtin_model_id(ImportModelClass::absorption); - + auto models = std::make_shared( + this->imported_data().optical_models); return std::make_shared(ActionId{0}, models); } - - ImportedModels::ImportedModelId import_model_id_; }; //---------------------------------------------------------------------------// @@ -80,17 +76,20 @@ TEST_F(AbsorptionModelTest, description) // Check absorption model MFP tables match imported ones TEST_F(AbsorptionModelTest, interaction_mfp) { + GridStorage storage; + auto model = create_model(); - auto builder = this->create_mfp_builder(); + auto builder = storage.create_mfp_builder(); - for (auto mat : range(OpticalMaterialId(import_materials().size()))) + for (auto mat : range(OpticalMaterialId(this->num_optical_materials()))) { model->build_mfps(mat, builder); } - this->check_built_table_exact( - this->import_models()[import_model_id_.get()].mfp_table, - builder.grid_ids()); + storage.check_built_table( + this->import_model_by_class(ImportModelClass::absorption).mfp_table, + builder.grid_ids(), + GridValidator::Exact); } //---------------------------------------------------------------------------// diff --git a/test/celeritas/optical/ImportedModelAdapter.test.cc b/test/celeritas/optical/ImportedModelAdapter.test.cc index 11ed4e5b26..948ffa7a39 100644 --- a/test/celeritas/optical/ImportedModelAdapter.test.cc +++ b/test/celeritas/optical/ImportedModelAdapter.test.cc @@ -13,7 +13,8 @@ #include "celeritas/ext/ScopedRootErrorHandler.hh" #include "celeritas/io/ImportData.hh" -#include "MockImportedData.hh" +#include "OpticalMockTestBase.hh" +#include "ValidationUtils.hh" #include "celeritas_test.hh" namespace celeritas @@ -27,10 +28,10 @@ using namespace ::celeritas::test; // TEST HARNESS //---------------------------------------------------------------------------// -class ImportedModelAdapterTest : public MockImportedData +class ImportedModelAdapterTest : public OpticalMockTestBase { protected: - void SetUp() override {} + using ImportedModelId = typename ImportedModels::ImportedModelId; void check_model(ImportOpticalModel const& expected_model, ImportOpticalModel const& imported_model) const @@ -40,10 +41,21 @@ class ImportedModelAdapterTest : public MockImportedData imported_model.mfp_table.size()); for (auto mat_id : range(imported_model.mfp_table.size())) { - this->check_mfp(expected_model.mfp_table[mat_id], - imported_model.mfp_table[mat_id]); + check_physics_vector(expected_model.mfp_table[mat_id], + imported_model.mfp_table[mat_id]); } } + + std::shared_ptr const& imported_models() const + { + static std::shared_ptr models; + if (!models) + { + models = std::make_shared( + this->imported_data().optical_models); + } + return models; + } }; //---------------------------------------------------------------------------// @@ -52,8 +64,8 @@ class ImportedModelAdapterTest : public MockImportedData // Create ImportedModels from mock data TEST_F(ImportedModelAdapterTest, build_mock) { - auto const& expected_models = this->import_models(); - auto imported_models = this->create_imported_models(); + auto const& expected_models = this->imported_data().optical_models; + auto imported_models = this->imported_models(); ASSERT_EQ(expected_models.size(), imported_models->num_models()); for (auto model_id : range(ImportedModelId{imported_models->num_models()})) @@ -71,7 +83,7 @@ TEST_F(ImportedModelAdapterTest, builtin_map) std::array expected_builtin_imcs{ IMC::absorption, IMC::rayleigh, IMC::wls}; - auto imported_models = this->create_imported_models(); + auto imported_models = this->imported_models(); // Check built-in models match expected ones EXPECT_EQ(expected_builtin_imcs.size(), static_cast(IMC::size_)); @@ -89,8 +101,8 @@ TEST_F(ImportedModelAdapterTest, builtin_map) // Check adapters correctly match MFPs TEST_F(ImportedModelAdapterTest, adapter_mfps) { - auto const& expected_models = this->import_models(); - auto imported_models = this->create_imported_models(); + auto const& expected_models = this->imported_data().optical_models; + auto imported_models = this->imported_models(); ASSERT_EQ(expected_models.size(), imported_models->num_models()); for (auto model_id : range(ImportedModelId{imported_models->num_models()})) @@ -101,8 +113,8 @@ TEST_F(ImportedModelAdapterTest, adapter_mfps) ASSERT_EQ(expected_model.mfp_table.size(), adapter.num_materials()); for (auto mat_id : range(OpticalMaterialId{adapter.num_materials()})) { - this->check_mfp(expected_model.mfp_table[mat_id.get()], - adapter.mfp(mat_id)); + check_physics_vector(expected_model.mfp_table[mat_id.get()], + adapter.mfp(mat_id)); } } } diff --git a/test/celeritas/optical/MfpBuilder.test.cc b/test/celeritas/optical/MfpBuilder.test.cc index 3efe8a4878..33c5268572 100644 --- a/test/celeritas/optical/MfpBuilder.test.cc +++ b/test/celeritas/optical/MfpBuilder.test.cc @@ -7,7 +7,8 @@ //---------------------------------------------------------------------------// #include "celeritas/optical/MfpBuilder.hh" -#include "MockImportedData.hh" +#include "OpticalMockTestBase.hh" +#include "ValidationUtils.hh" #include "celeritas_test.hh" namespace celeritas @@ -21,10 +22,8 @@ using namespace ::celeritas::test; // TEST HARNESS //---------------------------------------------------------------------------// -class MfpBuilderTest : public MockImportedData +class MfpBuilderTest : public OpticalMockTestBase { - protected: - void SetUp() override {} }; //---------------------------------------------------------------------------// @@ -33,13 +32,15 @@ class MfpBuilderTest : public MockImportedData // Check MFP tables are built with correct structure from imported data TEST_F(MfpBuilderTest, construct_tables) { - std::vector> tables; - auto const& models = this->import_models(); + GridStorage storage; + + std::vector> tables; + auto const& models = this->imported_data().optical_models; // Build MFP tables from imported data for (auto const& model : models) { - auto build = this->create_mfp_builder(); + auto build = storage.create_mfp_builder(); for (auto const& mfp : model.mfp_table) { @@ -54,7 +55,8 @@ TEST_F(MfpBuilderTest, construct_tables) // Check each MFP table has been built correctly for (auto table_id : range(tables.size())) { - this->check_built_table_exact(models[table_id].mfp_table, tables[table_id]); + storage.check_built_table( + models[table_id].mfp_table, tables[table_id], GridValidator::Exact); } } diff --git a/test/celeritas/optical/MockImportedData.cc b/test/celeritas/optical/MockImportedData.cc deleted file mode 100644 index 3d8d78a519..0000000000 --- a/test/celeritas/optical/MockImportedData.cc +++ /dev/null @@ -1,319 +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/MockImportedData.cc -//---------------------------------------------------------------------------// -#include "MockImportedData.hh" - -#include "TestMacros.hh" - -namespace celeritas -{ -namespace optical -{ -namespace test -{ -//---------------------------------------------------------------------------// -struct MeterCubedPerMeV -{ - static CELER_CONSTEXPR_FUNCTION real_type value() - { - return ipow<3>(units::meter) / units::Mev::value(); - } - - static char const* label() { return "m^3/MeV"; } -}; - -using IsothermalCompressibility = Quantity; - -//---------------------------------------------------------------------------// -/*! - * Convert vector of some floating type to a vector of reals. - */ -template -std::vector convert_to_reals(std::vector const& xs) -{ - std::vector reals; - reals.reserve(xs.size()); - for (T x : xs) - { - reals.push_back(static_cast(x)); - } - return reals; -} - -//---------------------------------------------------------------------------// -/*! - * Create some mock physics vectors. - * - * For x grid: - * i = 0,1: size = 2 - * i = 2,3: size = 3 - * - * For y values: - * j = 0,1,2: size = 2 - * j = 3,4: size = 3 - */ -std::vector -mock_vec(std::vector const& grid_indices, - std::vector const& value_indices) -{ - static std::vector> grids{ - {1e-3, 1e2}, {1e-2, 3e2}, {2e-3, 5e-1, 1e2}, {1e-3, 2e-3, 5e-1}}; - - static std::vector> values{ - {5.7, 9.3}, - {1.2, 10.7}, - {3.1, 5.4}, - {0.1, 7.6, 12.5}, - {1.3, 4.9, 9.4}, - }; - - std::vector> mock_grids; - for (auto i : grid_indices) - { - CELER_ASSERT(i < grids.size()); - mock_grids.push_back(grids[i]); - } - - std::vector> mock_values; - for (auto j : value_indices) - { - CELER_ASSERT(j < values.size()); - mock_values.push_back(values[j]); - } - - return detail::convert_vector_units( - mock_grids, mock_values); -} - -//---------------------------------------------------------------------------// -/*! - * Construct vector of ImportOpticalModel from mock data. - * - * There are 4 imported models, one for each optical model class. All models - * have MFP grids for 5 materials. - */ -std::vector const& MockImportedData::import_models() -{ - using IMC = ImportModelClass; - - static std::vector models{ - {IMC::absorption, mock_vec({0, 1, 1, 2, 3}, {0, 1, 2, 3, 4})}, - {IMC::rayleigh, mock_vec({1, 0, 3, 2, 0}, {0, 1, 3, 3, 2})}, - {IMC::wls, mock_vec({3, 1, 1, 2, 3}, {4, 0, 1, 4, 4})}}; - - return models; -} - -//---------------------------------------------------------------------------// -/*! - * Construct vector of ImportOpticalMaterial from mock data. - */ -std::vector const& MockImportedData::import_materials() -{ - using namespace celeritas::units; - - static std::vector> mock_energies - = {{1.098177, 1.256172, 1.484130}, {1.098177, 6.812319}, {1, 2, 5}}; - - static std::vector> mock_rindex - = {{1.3235601610672, 1.3256740639273, 1.3280120256415}, - {1.3235601610672, 1.4679465862259}, - {1.3, 1.4, 1.5}}; - - auto properties - = detail::convert_vector_units( - mock_energies, mock_rindex); - - static ImportOpticalRayleigh mock_rayleigh[] - = {{1, 7.658e-23 * MeterCubedPerMeV::value()}, - {1.7, 4.213e-24 * MeterCubedPerMeV::value()}, - {2, 1e-20 * MeterCubedPerMeV::value()}}; - - static std::vector materials{ - ImportOpticalMaterial{ImportOpticalProperty{properties[0]}, - ImportScintData{}, - ImportOpticalRayleigh{mock_rayleigh[0]}, - ImportWavelengthShift{}}, - ImportOpticalMaterial{ImportOpticalProperty{properties[0]}, - ImportScintData{}, - ImportOpticalRayleigh{mock_rayleigh[1]}, - ImportWavelengthShift{}}, - ImportOpticalMaterial{ImportOpticalProperty{properties[1]}, - ImportScintData{}, - ImportOpticalRayleigh{mock_rayleigh[0]}, - ImportWavelengthShift{}}, - ImportOpticalMaterial{ImportOpticalProperty{properties[2]}, - ImportScintData{}, - ImportOpticalRayleigh{mock_rayleigh[2]}, - ImportWavelengthShift{}}, - ImportOpticalMaterial{ImportOpticalProperty{properties[1]}, - ImportScintData{}, - ImportOpticalRayleigh{mock_rayleigh[1]}, - ImportWavelengthShift{}}}; - - return materials; -} - -//---------------------------------------------------------------------------// -/*! - * Retrieve optical materials constructed from mock imported data. - * - * Will only construct the materials when called, and only once. - */ -auto MockImportedData::optical_materials() const -> SPConstMaterials const& -{ - static SPConstMaterials materials = nullptr; - if (!materials) - { - materials = this->build_optical_materials(); - } - return materials; -} - -//---------------------------------------------------------------------------// -/*! - * Build optical material parameters from imported data. - */ -auto MockImportedData::build_optical_materials() const -> SPConstMaterials -{ - MaterialParams::Input input; - for (auto mat : MockImportedData::import_materials()) - { - input.properties.push_back(mat.properties); - } - - // Volume -> optical material mapping with some redundancies - for (auto opt_mat : range(8)) - { - input.volume_to_mat.push_back( - OpticalMaterialId(opt_mat % input.properties.size())); - } - - return std::make_shared(std::move(input)); -} - -//---------------------------------------------------------------------------// -/*! - * Create ImportedModels all with empty MFP grids. - * - * Useful for testing optical models which build their MFPs from material data. - */ -auto MockImportedData::create_empty_imported_models() const -> SPConstImported -{ - std::vector empty_models; - empty_models.reserve(this->import_models().size()); - ImportPhysicsVector const empty_vec{ImportPhysicsVectorType::free, {}, {}}; - for (auto const& model : this->import_models()) - { - empty_models.push_back( - {model.model_class, - ImportedMfpTable(model.mfp_table.size(), empty_vec)}); - } - - return std::make_shared(std::move(empty_models)); -} - -//---------------------------------------------------------------------------// -/*! - * Create ImportedModels from mock data. - */ -auto MockImportedData::create_imported_models() const -> SPConstImported -{ - return std::make_shared(this->import_models()); -} - -//---------------------------------------------------------------------------// -/*! - * Create an MFP builder that uses this object's collections. - */ -auto MockImportedData::create_mfp_builder() -> MfpBuilder -{ - return MfpBuilder(&reals, &grids); -} - -//---------------------------------------------------------------------------// -/*! - * Check that two MFP physics vectors are equal. - */ -void MockImportedData::check_mfp(ImportPhysicsVector const& expected, - ImportPhysicsVector const& imported) const -{ - EXPECT_EQ(expected.vector_type, imported.vector_type); - EXPECT_VEC_EQ(expected.x, imported.x); - EXPECT_VEC_EQ(expected.y, imported.y); -} - -//---------------------------------------------------------------------------// -/*! - * Check that the physics table built in the collections matches the - * imported MFP table it was built from. - */ -void MockImportedData::check_built_table(ImportedMfpTable const& expected_mfps, - ItemRange const& table, - bool soft) const -{ - // Each MFP has a built grid - ASSERT_EQ(expected_mfps.size(), table.size()); - - for (auto mfp_id : range(expected_mfps.size())) - { - // Grid IDs should be valid - auto grid_id = table[mfp_id]; - ASSERT_LT(grid_id, grids.size()); - - // Grid should be valid - Grid const& grid = grids[grid_id]; - ASSERT_TRUE(grid); - - // Grid ranges should be valid - ASSERT_LT(grid.grid.back(), reals.size()); - ASSERT_LT(grid.value.back(), reals.size()); - - // Convert imported data to real_type for comparison - auto const& expected_mfp = expected_mfps[mfp_id]; - std::vector expected_grid = convert_to_reals(expected_mfp.x); - std::vector expected_value - = convert_to_reals(expected_mfp.y); - - // Built grid data should match expected grid data - if (soft) - { - EXPECT_VEC_SOFT_EQ(expected_grid, reals[grid.grid]); - EXPECT_VEC_SOFT_EQ(expected_value, reals[grid.value]); - } - else - { - EXPECT_VEC_EQ(expected_grid, reals[grid.grid]); - EXPECT_VEC_EQ(expected_value, reals[grid.value]); - } - } -} - -//---------------------------------------------------------------------------// -/*! - * Check the built physics table with soft equality. - */ -void MockImportedData::check_built_table_soft(ImportedMfpTable const& expected, - ItemRange const& table) const -{ - this->check_built_table(expected, table, true); -} - -//---------------------------------------------------------------------------// -/*! - * Check the built physics table with exact equality. - */ -void MockImportedData::check_built_table_exact( - ImportedMfpTable const& expected, ItemRange const& table) const -{ - this->check_built_table(expected, table, false); -} - -//---------------------------------------------------------------------------// -} // namespace test -} // namespace optical -} // namespace celeritas diff --git a/test/celeritas/optical/MockImportedData.hh b/test/celeritas/optical/MockImportedData.hh deleted file mode 100644 index afefe7fc2f..0000000000 --- a/test/celeritas/optical/MockImportedData.hh +++ /dev/null @@ -1,143 +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/MockImportedData.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "corecel/data/Collection.hh" -#include "celeritas/UnitTypes.hh" -#include "celeritas/grid/GenericGridData.hh" -#include "celeritas/io/ImportOpticalMaterial.hh" -#include "celeritas/io/ImportOpticalModel.hh" -#include "celeritas/io/ImportPhysicsVector.hh" -#include "celeritas/optical/ImportedModelAdapter.hh" -#include "celeritas/optical/MaterialParams.hh" -#include "celeritas/optical/MfpBuilder.hh" - -#include "Test.hh" - -namespace celeritas -{ -namespace optical -{ -namespace test -{ -using namespace ::celeritas::test; - -//---------------------------------------------------------------------------// -/*! - * Imported mock optical data. - * - * A base class that provides common mock data and functionality for testing - * optical physics. - */ -class MockImportedData : public ::celeritas::test::Test -{ - protected: - //!@{ - //! \name Type aliases - using Grid = GenericGridRecord; - using GridId = OpaqueId; - - using ImportedMfpTable = std::vector; - - using ImportedModelId = typename ImportedModels::ImportedModelId; - using SPConstImported = std::shared_ptr; - using SPConstMaterials = std::shared_ptr; - - template - using Items = Collection; - //!@} - - protected: - //!@{ - //! \name Access mock data - static std::vector const& import_models(); - static std::vector const& import_materials(); - std::shared_ptr const& optical_materials() const; - //!@} - - //!@{ - //! \name Construct commonly used objects - SPConstImported create_empty_imported_models() const; - SPConstImported create_imported_models() const; - MfpBuilder create_mfp_builder(); - SPConstMaterials build_optical_materials() const; - //!@} - - //!@{ - //! \name Check results - void check_mfp(ImportPhysicsVector const& expected, - ImportPhysicsVector const& imported) const; - void check_built_table_exact(ImportedMfpTable const& expected, - ItemRange const& table) const; - void check_built_table_soft(ImportedMfpTable const& expected, - ItemRange const& table) const; - void check_built_table(ImportedMfpTable const& expected, - ItemRange const& table, - bool soft) const; - //!@} - - //!@{ - //! \name Storage data - Items reals; - Items grids; - //!@} -}; - -namespace detail -{ -//---------------------------------------------------------------------------// -/*! - * Useful unit for working in optical physics scales. - */ -struct ElectronVolt -{ - static CELER_CONSTEXPR_FUNCTION real_type value() - { - return units::Mev::value() / 1e6; - } - static char const* label() { return "eV"; } -}; - -//---------------------------------------------------------------------------// -/*! - * Takes a list of grids and values in the specified template units, - * and converts to an ImportPhysicsVector of in Celeritas' units. - * - * The grid x units are returned in [MeV] rather than native, matching - * the usual unit for imported grids. - */ -template -std::vector -convert_vector_units(std::vector> const& grid, - std::vector> const& value) -{ - CELER_ASSERT(grid.size() == value.size()); - std::vector vecs; - for (auto i : range(grid.size())) - { - ImportPhysicsVector v; - v.vector_type = ImportPhysicsVectorType::free; - for (double x : grid[i]) - { - v.x.push_back(x * GridUnit::value() / units::Mev::value()); - } - for (double y : value[i]) - { - v.y.push_back(y * ValueUnit::value()); - } - CELER_ASSERT(v); - vecs.push_back(std::move(v)); - } - return vecs; -} - -//---------------------------------------------------------------------------// -} // namespace detail -} // namespace test -} // namespace optical -} // namespace celeritas diff --git a/test/celeritas/optical/MockValidation.test.cc b/test/celeritas/optical/MockValidation.test.cc deleted file mode 100644 index 7e0c83e4a0..0000000000 --- a/test/celeritas/optical/MockValidation.test.cc +++ /dev/null @@ -1,66 +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/MockValidation.test.cc -//---------------------------------------------------------------------------// -#include "MockImportedData.hh" -#include "celeritas_test.hh" - -namespace celeritas -{ -namespace optical -{ -namespace test -{ -using namespace ::celeritas::test; -//---------------------------------------------------------------------------// -// TEST HARNESS -//---------------------------------------------------------------------------// - -class MockValidationTest : public MockImportedData -{ - protected: - void SetUp() override {} -}; - -//---------------------------------------------------------------------------// -// TESTS -//---------------------------------------------------------------------------// -// Validate that the mock optical data makes sense -TEST_F(MockValidationTest, validate) -{ - auto const& models = import_models(); - auto const& materials = import_materials(); - - EXPECT_EQ(3, models.size()); - EXPECT_EQ(5, materials.size()); - - // Check models - - for (auto const& model : models) - { - EXPECT_NE(ImportModelClass::size_, model.model_class); - EXPECT_EQ(materials.size(), model.mfp_table.size()); - - for (auto const& mfp : model.mfp_table) - { - EXPECT_EQ(ImportPhysicsVectorType::free, mfp.vector_type); - EXPECT_TRUE(mfp); - } - } - - // Check materials - - for (auto const& material : materials) - { - EXPECT_TRUE(material.properties); - EXPECT_TRUE(material.rayleigh); - } -} - -//---------------------------------------------------------------------------// -} // namespace test -} // namespace optical -} // namespace celeritas diff --git a/test/celeritas/optical/OpticalMockTestBase.cc b/test/celeritas/optical/OpticalMockTestBase.cc new file mode 100644 index 0000000000..ac87227ffd --- /dev/null +++ b/test/celeritas/optical/OpticalMockTestBase.cc @@ -0,0 +1,281 @@ +//----------------------------------*-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/OpticalMockTestBase.cc +//---------------------------------------------------------------------------// +#include "OpticalMockTestBase.hh" + +#include "celeritas/io/ImportOpticalMaterial.hh" +#include "celeritas/io/ImportOpticalModel.hh" + +#include "ValidationUtils.hh" + +namespace celeritas +{ +namespace optical +{ +namespace test +{ +//---------------------------------------------------------------------------// +// UNITS +//---------------------------------------------------------------------------// +struct ElectronVolt +{ + static CELER_CONSTEXPR_FUNCTION real_type value() + { + return units::Mev::value() / 1e6; + } + static char const* label() { return "eV"; } +}; + +struct Kelvin +{ + static CELER_CONSTEXPR_FUNCTION real_type value() { return units::kelvin; } + + static char const* label() { return "K"; } +}; + +struct MeterCubedPerMeV +{ + static CELER_CONSTEXPR_FUNCTION real_type value() + { + return ipow<3>(units::meter) / units::Mev::value(); + } + + static char const* label() { return "m^3/MeV"; } +}; + +//---------------------------------------------------------------------------// +// HELPER FUNCTIONS +//---------------------------------------------------------------------------// +/*! + * Helper function for converting hardcoded grids into \c ImportPhysicsVector. + * + * The grid energy is converted to units of MeV, while the values are converted + * to native units. + */ +template +ImportPhysicsVector +physics_vector_expressed_as(std::vector xs, std::vector ys) +{ + CELER_EXPECT(xs.size() == ys.size()); + ImportPhysicsVector v{ + ImportPhysicsVectorType::free, std::move(xs), std::move(ys)}; + for (double& x : v.x) + { + x = value_as(native_value_to( + native_value_from(Quantity{x}))); + } + + for (double& y : v.y) + { + y = native_value_from(Quantity{y}); + } + + return v; +} + +//---------------------------------------------------------------------------// +/*! + * Helper function for converting hardcoded tables (lists of grids) into + * \c ImportPhysicsVector. + */ +template +std::vector physics_table_expressed_as( + std::vector, std::vector>> data) +{ + std::vector table; + table.reserve(data.size()); + for (auto&& arrs : data) + { + table.push_back(physics_vector_expressed_as( + std::move(std::get<0>(arrs)), std::move(std::get<1>(arrs)))); + } + + return table; +} + +//---------------------------------------------------------------------------// +// OpticalMockTestBase +//---------------------------------------------------------------------------// +/*! + * Constructs optical material parameters from mock data. + */ +auto OpticalMockTestBase::build_optical_material() -> SPConstOpticalMaterial +{ + MaterialParams::Input input; + for (auto mat : this->imported_data().optical_materials) + { + input.properties.push_back(mat.properties); + } + + // Volume -> optical material mapping with some redundancies + for (auto opt_mat : range(8)) + { + input.volume_to_mat.push_back( + OpticalMaterialId(opt_mat % input.properties.size())); + } + + return std::make_shared(std::move(input)); +} + +//---------------------------------------------------------------------------// +/*! + * Constructs (core) material parameters from mock data. + * + * Only temperatures and optical material IDs are assigned meaningful values. + */ +auto OpticalMockTestBase::build_material() -> SPConstMaterial +{ + ::celeritas::MaterialParams::Input input; + + static constexpr auto material_temperatures + = expressed_as>(283.15, 300.0, 283.15, 200., 300.0); + + // Unused element - only to pass checks + input.elements.push_back(::celeritas::MaterialParams::ElementInput{ + AtomicNumber{1}, units::AmuMass{1}, {}, "fake"}); + + for (auto i : range(material_temperatures.size())) + { + // Only temperature is relevant information + input.materials.push_back(::celeritas::MaterialParams::MaterialInput{ + 0, + material_temperatures[i], + MatterState::solid, + {}, + std::to_string(i).c_str()}); + + // mock MaterialId == OpticalMaterialId + input.mat_to_optical.push_back(OpticalMaterialId{i}); + } + + return std::make_shared<::celeritas::MaterialParams const>( + std::move(input)); +} + +//---------------------------------------------------------------------------// +/*! + * Access mock imported data. + */ +ImportData const& OpticalMockTestBase::imported_data() const +{ + static ImportData data; + if (data.optical_materials.empty()) + { + this->build_import_data(data); + } + return data; +} + +//---------------------------------------------------------------------------// +/*! + * Create mock imported data in-place. + */ +void OpticalMockTestBase::build_import_data(ImportData& data) const +{ + // Build mock imported optical materials + { + data.optical_materials = std::vector(5); + + data.optical_materials[0].properties.refractive_index + = physics_vector_expressed_as( + {1.098177, 1.256172, 1.484130}, + {1.3235601610672, 1.3256740639273, 1.3280120256415}); + data.optical_materials[0].rayleigh.scale_factor = 1; + data.optical_materials[0].rayleigh.compressibility + = native_value_from(Quantity{7.658e-23}); + + data.optical_materials[1].properties.refractive_index + = physics_vector_expressed_as( + {1.098177, 1.256172, 1.484130}, + {1.3235601610672, 1.3256740639273, 1.3280120256415}); + data.optical_materials[1].rayleigh.scale_factor = 1.7; + data.optical_materials[1].rayleigh.compressibility + = native_value_from(Quantity{4.213e-24}); + + data.optical_materials[2].properties.refractive_index + = physics_vector_expressed_as( + {1.098177, 6.812319}, {1.3235601610672, 1.4679465862259}); + data.optical_materials[2].rayleigh.scale_factor = 1; + data.optical_materials[2].rayleigh.compressibility + = native_value_from(Quantity{7.658e-23}); + + data.optical_materials[3].properties.refractive_index + = physics_vector_expressed_as( + {1, 2, 5}, {1.3, 1.4, 1.5}); + data.optical_materials[3].rayleigh.scale_factor = 2; + data.optical_materials[3].rayleigh.compressibility + = native_value_from(Quantity{1e-20}); + + data.optical_materials[4].properties.refractive_index + = physics_vector_expressed_as( + {1.098177, 6.812319}, {1.3235601610672, 1.4679465862259}); + data.optical_materials[4].rayleigh.scale_factor = 1.7; + data.optical_materials[4].rayleigh.compressibility + = native_value_from(Quantity{4.213e-24}); + } + + // Build mock imported optical models + { + data.optical_models = std::vector(3); + + data.optical_models[0].model_class = ImportModelClass::absorption; + data.optical_models[0].mfp_table + = physics_table_expressed_as({ + {{1e-3, 1e-2}, {5.7, 9.3}}, + {{1e-2, 3e2}, {1.2, 10.7}}, + {{1e-2, 3e2}, {3.1, 5.4}}, + {{2e-3, 5e1, 1e2}, {0.1, 7.6, 12.5}}, + {{1e-3, 2e-3, 5e-1}, {1.3, 4.9, 9.4}}, + }); + + data.optical_models[1].model_class = ImportModelClass::rayleigh; + data.optical_models[1].mfp_table + = physics_table_expressed_as({ + {{1e-2, 3e2}, {5.7, 9.3}}, + {{1e-3, 1e-2}, {1.2, 10.7}}, + {{1e-3, 2e-3, 5e-1}, {0.1, 7.6, 12.5}}, + {{2e-3, 5e1, 1e2}, {0.1, 7.6, 12.5}}, + {{1e-3, 1e-2}, {3.1, 5.4}}, + }); + + data.optical_models[2].model_class = ImportModelClass::wls; + data.optical_models[2].mfp_table + = physics_table_expressed_as({ + {{1e-3, 2e-3, 5e-1}, {1.3, 4.9, 9.4}}, + {{1e-2, 3e2}, {5.7, 9.3}}, + {{1e-2, 3e2}, {1.2, 10.7}}, + {{2e-3, 5e1, 1e2}, {1.3, 4.9, 9.4}}, + {{1e-3, 2e-3, 5e-1}, {1.3, 4.9, 9.4}}, + }); + } +} + +//---------------------------------------------------------------------------// +/*! + * Get the imported optical model corresponding to the given \c + * ImportModelClass. + */ +ImportOpticalModel const& +OpticalMockTestBase::import_model_by_class(ImportModelClass imc) const +{ + switch (imc) + { + case ImportModelClass::absorption: + return this->imported_data().optical_models[0]; + case ImportModelClass::rayleigh: + return this->imported_data().optical_models[1]; + case ImportModelClass::wls: + return this->imported_data().optical_models[2]; + default: + CELER_ASSERT_UNREACHABLE(); + } +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace optical +} // namespace celeritas diff --git a/test/celeritas/optical/OpticalMockTestBase.hh b/test/celeritas/optical/OpticalMockTestBase.hh new file mode 100644 index 0000000000..dddbfdb499 --- /dev/null +++ b/test/celeritas/optical/OpticalMockTestBase.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/optical/OpticalMockTestBase.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/io/ImportData.hh" +#include "celeritas/mat/MaterialParams.hh" +#include "celeritas/optical/MaterialParams.hh" + +#include "../GlobalTestBase.hh" + +namespace celeritas +{ +namespace optical +{ +namespace test +{ +using namespace ::celeritas::test; +//---------------------------------------------------------------------------// +/*! + * Class containing mock test data for optical physics. + */ +class OpticalMockTestBase : public GlobalTestBase +{ + public: + // Construct optical material parameters from mock data + SPConstOpticalMaterial build_optical_material() override; + + // Construct (core) material parameters from mock data + SPConstMaterial build_material() override; + + // Access mock imported data + ImportData const& imported_data() const; + + // Retrieve imported optical model data by class + ImportOpticalModel const& import_model_by_class(ImportModelClass) const; + + //! Number of mock optical materials + inline OpticalMaterialId::size_type num_optical_materials() const + { + return this->imported_data().optical_materials.size(); + } + + //!@{ + //! \name Unsupported params builders + SPConstGeo build_geometry() override { CELER_ASSERT_UNREACHABLE(); } + SPConstGeoMaterial build_geomaterial() override + { + CELER_ASSERT_UNREACHABLE(); + } + SPConstParticle build_particle() override { CELER_ASSERT_UNREACHABLE(); } + SPConstCutoff build_cutoff() override { CELER_ASSERT_UNREACHABLE(); } + SPConstPhysics build_physics() override { CELER_ASSERT_UNREACHABLE(); } + SPConstSim build_sim() override { CELER_ASSERT_UNREACHABLE(); } + SPConstTrackInit build_init() override { CELER_ASSERT_UNREACHABLE(); } + SPConstWentzelOKVI build_wentzel() override { CELER_ASSERT_UNREACHABLE(); } + SPConstAction build_along_step() override { CELER_ASSERT_UNREACHABLE(); } + SPConstCerenkov build_cerenkov() override { CELER_ASSERT_UNREACHABLE(); } + SPConstScintillation build_scintillation() override + { + CELER_ASSERT_UNREACHABLE(); + } + //!@} + + private: + // Construct mock import data in place + void build_import_data(ImportData&) const; +}; + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace optical +} // namespace celeritas diff --git a/test/celeritas/optical/Rayleigh.test.cc b/test/celeritas/optical/Rayleigh.test.cc index 81ff352125..a1347bf5af 100644 --- a/test/celeritas/optical/Rayleigh.test.cc +++ b/test/celeritas/optical/Rayleigh.test.cc @@ -10,7 +10,8 @@ #include "celeritas/optical/model/RayleighModel.hh" #include "InteractorHostTestBase.hh" -#include "MockImportedData.hh" +#include "OpticalMockTestBase.hh" +#include "ValidationUtils.hh" #include "celeritas_test.hh" namespace celeritas @@ -42,7 +43,7 @@ class RayleighInteractorTest : public InteractorHostTestBase } }; -class RayleighModelTest : public MockImportedData +class RayleighModelTest : public OpticalMockTestBase { protected: void SetUp() override {} @@ -50,19 +51,17 @@ class RayleighModelTest : public MockImportedData //! Create Rayleigh model from mock data std::shared_ptr create_model() { - auto models = this->create_imported_models(); - import_model_id_ = models->builtin_model_id(ImportModelClass::rayleigh); + auto models = std::make_shared( + this->imported_data().optical_models); return std::make_shared(ActionId{0}, models); } - - ImportedModels::ImportedModelId import_model_id_; }; //---------------------------------------------------------------------------// // TESTS //---------------------------------------------------------------------------// // Basic tests for Rayleigh scattering interaction -TEST_F(RayleighInteractorTest, basic) +TEST_F(RayleighInteractorTest, TEST_IF_CELERITAS_DOUBLE(basic)) { int const num_samples = 4; @@ -103,7 +102,7 @@ TEST_F(RayleighInteractorTest, basic) //---------------------------------------------------------------------------// // Test statistical consistency over larger number of samples -TEST_F(RayleighInteractorTest, stress_test) +TEST_F(RayleighInteractorTest, TEST_IF_CELERITAS_DOUBLE(stress_test)) { int const num_samples = 1'000; @@ -157,17 +156,20 @@ TEST_F(RayleighModelTest, description) // Check Rayleigh model MFP tables match imported ones TEST_F(RayleighModelTest, interaction_mfp) { + GridStorage storage; + auto model = create_model(); - auto builder = this->create_mfp_builder(); + auto builder = storage.create_mfp_builder(); - for (auto mat : range(OpticalMaterialId(import_materials().size()))) + for (auto mat : range(OpticalMaterialId(this->num_optical_materials()))) { model->build_mfps(mat, builder); } - this->check_built_table_exact( - this->import_models()[import_model_id_.get()].mfp_table, - builder.grid_ids()); + storage.check_built_table( + this->import_model_by_class(ImportModelClass::rayleigh).mfp_table, + builder.grid_ids(), + GridValidator::Exact); } //---------------------------------------------------------------------------// diff --git a/test/celeritas/optical/RayleighMfpCalculator.test.cc b/test/celeritas/optical/RayleighMfpCalculator.test.cc index b56cfffcaa..9e145bc94d 100644 --- a/test/celeritas/optical/RayleighMfpCalculator.test.cc +++ b/test/celeritas/optical/RayleighMfpCalculator.test.cc @@ -9,7 +9,8 @@ #include "celeritas/mat/MaterialParams.hh" -#include "MockImportedData.hh" +#include "OpticalMockTestBase.hh" +#include "ValidationUtils.hh" #include "celeritas_test.hh" namespace celeritas @@ -23,43 +24,8 @@ using namespace ::celeritas::test; // TEST HARNESS //---------------------------------------------------------------------------// -class RayleighMfpCalculatorTest : public MockImportedData +class RayleighMfpCalculatorTest : public OpticalMockTestBase { - protected: - using SPConstCoreMaterials - = std::shared_ptr<::celeritas::MaterialParams const>; - - void SetUp() override {} - - SPConstCoreMaterials build_core_materials() const - { - ::celeritas::MaterialParams::Input input; - - static real_type const material_temperatures[] - = {283.15, 300.0, 283.15, 200, 300.0}; - - // Unused element - only to pass checks - input.elements.push_back(::celeritas::MaterialParams::ElementInput{ - AtomicNumber{1}, units::AmuMass{1}, {}, "fake"}); - - for (auto i : range(size_type{5})) - { - // Only temperature is relevant information - input.materials.push_back( - ::celeritas::MaterialParams::MaterialInput{ - 0, - material_temperatures[i] * units::kelvin, - MatterState::solid, - {}, - std::to_string(i).c_str()}); - - // mock MaterialId == OpticalMaterialId - input.mat_to_optical.push_back(OpticalMaterialId{i}); - } - - return std::make_shared<::celeritas::MaterialParams const>( - std::move(input)); - } }; //---------------------------------------------------------------------------// @@ -68,40 +34,44 @@ class RayleighMfpCalculatorTest : public MockImportedData // Check calculated MFPs match expected ones TEST_F(RayleighMfpCalculatorTest, mfp_table) { - static std::vector> expected_tables - = {{1189584.7068151, 682569.13017288, 343507.60086802}, - {12005096.767467, 6888377.4406869, 3466623.2384762}, - {1189584.7068151, 277.60444893823}, - {11510.805603078, 322.70360179716, 4.230373664558}, - {12005096.767467, 2801.539271218}}; - - auto core_materials = this->build_core_materials(); - - for (auto opt_mat : range(OpticalMaterialId(import_materials().size()))) + static constexpr auto expected_mfps + = expressed_as(1189584.7068151, + 682569.13017288, + 343507.60086802, + 12005096.767467, + 6888377.4406869, + 3466623.2384762, + 1189584.7068151, + 277.60444893823, + 11510.805603078, + 322.70360179716, + 4.230373664558, + 12005096.767467, + 2801.539271218); + + auto core_materials = this->material(); + auto const& opt_materials = this->imported_data().optical_materials; + + std::vector mfps; + mfps.reserve(expected_mfps.size()); + + for (auto opt_mat : range(OpticalMaterialId(opt_materials.size()))) { - auto const& rayleigh = import_materials()[opt_mat.get()].rayleigh; + auto const& rayleigh = opt_materials[opt_mat.get()].rayleigh; RayleighMfpCalculator calc_mfp( - MaterialView(this->optical_materials()->host_ref(), opt_mat), + MaterialView(this->optical_material()->host_ref(), opt_mat), rayleigh, - ::celeritas::MaterialView(core_materials->host_ref(), - ::celeritas::MaterialId(opt_mat.get()))); + core_materials->get(::celeritas::MaterialId(opt_mat.get()))); auto energies = calc_mfp.grid().values(); - auto const& table = expected_tables[opt_mat.get()]; - - ASSERT_EQ(energies.size(), table.size()); - - std::vector expected_mfps(energies.size(), 0); - std::vector mfps(energies.size(), 0); for (auto i : range(energies.size())) { - expected_mfps[i] = table[i] * units::Centimeter::value(); - mfps[i] = calc_mfp(units::MevEnergy{energies[i]}); + mfps.push_back(calc_mfp(units::MevEnergy{energies[i]})); } - - EXPECT_VEC_SOFT_EQ(expected_mfps, mfps); } + + EXPECT_VEC_SOFT_EQ(expected_mfps, mfps); } //---------------------------------------------------------------------------// diff --git a/test/celeritas/optical/ValidationUtils.cc b/test/celeritas/optical/ValidationUtils.cc new file mode 100644 index 0000000000..5cb5ed3682 --- /dev/null +++ b/test/celeritas/optical/ValidationUtils.cc @@ -0,0 +1,111 @@ +//----------------------------------*-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/ValidationUtils.cc +//---------------------------------------------------------------------------// +#include "ValidationUtils.hh" + +namespace celeritas +{ +namespace optical +{ +namespace test +{ +using namespace ::celeritas::test; +//---------------------------------------------------------------------------// +/*! + */ +void check_physics_vector(ImportPhysicsVector const& expected, + ImportPhysicsVector const& actual) +{ + EXPECT_EQ(expected.vector_type, actual.vector_type); + EXPECT_VEC_EQ(expected.x, actual.x); + EXPECT_VEC_EQ(expected.y, actual.y); +} + +//---------------------------------------------------------------------------// +/*! + * Construct validator for with the underlying storage. + */ +GridValidator::GridValidator(Items* reals, Items* grids) + : reals_(reals), grids_(grids) +{ + CELER_EXPECT(reals_); + CELER_EXPECT(grids_); +} + +//---------------------------------------------------------------------------// +/*! + * Check the imported data is built under the given grid ID range. + */ +void GridValidator::check_built_table(ImportPhysicsTable const& table, + ItemRange grid_ids, + Softness soft) +{ + ASSERT_EQ(table.size(), grid_ids.size()); + + for (auto i : range(grid_ids.size())) + { + this->check_built_grid(table[i], grid_ids[i], soft); + } +} + +//---------------------------------------------------------------------------// +/*! + * Check the imported data is built under the given ID range. + */ +void GridValidator::check_built_grid(ImportPhysicsVector const& expected, + GridId grid_id, + Softness soft) +{ + ASSERT_LT(grid_id, grids_->size()); + Grid const& grid = (*grids_)[grid_id]; + ASSERT_TRUE(grid); + + this->check_built_vector(expected.x, grid.grid, soft); + this->check_built_vector(expected.y, grid.value, soft); +} + +//---------------------------------------------------------------------------// +/*! + * Construct an MFP builder with the underlying collections. + */ +MfpBuilder GridValidator::create_mfp_builder() +{ + return MfpBuilder(reals_, grids_); +} + +//---------------------------------------------------------------------------// +/*! + * Check the imported data is built in the given data range. + */ +void GridValidator::check_span(Span const& t, + ItemRange const& real_ids, + Softness soft) +{ + ASSERT_LT(real_ids.front(), real_ids.back()); + ASSERT_LT(real_ids.back(), reals_->size()); + + switch (soft) + { + case Soft: + EXPECT_VEC_SOFT_EQ(t, (*reals_)[real_ids]); + break; + case Exact: + EXPECT_VEC_EQ(t, (*reals_)[real_ids]); + break; + } +} + +//---------------------------------------------------------------------------// +/*! + * Construct with internal collections. + */ +GridStorage::GridStorage() : GridValidator(&reals_, &grids_) {} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace optical +} // namespace celeritas diff --git a/test/celeritas/optical/ValidationUtils.hh b/test/celeritas/optical/ValidationUtils.hh new file mode 100644 index 0000000000..4aaf7483a8 --- /dev/null +++ b/test/celeritas/optical/ValidationUtils.hh @@ -0,0 +1,184 @@ +//----------------------------------*-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/ValidationUtils.hh +//---------------------------------------------------------------------------// +#pragma once + +#include + +#include "corecel/cont/Span.hh" +#include "corecel/data/Collection.hh" +#include "celeritas/UnitTypes.hh" +#include "celeritas/grid/GenericGridData.hh" +#include "celeritas/io/ImportPhysicsVector.hh" +#include "celeritas/optical/MfpBuilder.hh" + +#include "Test.hh" +#include "TestMacros.hh" + +namespace celeritas +{ +namespace optical +{ +namespace test +{ +using namespace ::celeritas::test; +//---------------------------------------------------------------------------// +/*! + * Helper function to annotate units of a hard-coded test data array. + * + * Converts the arguments supplied in units \c UnitType to native units. + */ +template +std::array constexpr expressed_as(Args const&... args) +{ + return std::array{ + native_value_from(UnitType{args})...}; +} + +//---------------------------------------------------------------------------// +/*! + * Helper function to annotate units of hard-coded test data. + * + * Same as \c expressed_as, except returns a vector. + */ +template +std::vector vector_expressed_as(Args const&... args) +{ + return std::vector{native_value_from(UnitType{args})...}; +} + +//---------------------------------------------------------------------------// +/*! + * Checks two \c ImportPhysicVector for exact equality. + */ +void check_physics_vector(ImportPhysicsVector const& expected, + ImportPhysicsVector const& actual); + +//---------------------------------------------------------------------------// +/*! + * Convert a floating point type collection into a \c real_type span. + * + * Used to convert data which may be expressed as double or single precision + * into the same size as \c real_type. If the supplied data is the same + * precision, then just a span to the original data is returned. Otherwise, + * it is copied into a local buffer and casted to the correct precision. The + * \c RealSpanGenerator object should live as long as its spans are being + * used in the tests. + */ +class RealSpanGenerator +{ + public: + /*! + * Create a \c real_type \c Span for the supplied collection type \c T. + * + * If \c T::value_type is \c real_type, then a span to the original data + * is returned. Otherwise, it is copied to a local buffer and cast to + * \c real_type. + */ + template + Span operator()(T const& xs) + { + if constexpr (std::is_same_v>) + { + return make_span(xs); + } + else + { + buffer_.resize(xs.size()); + for (auto i : range(xs.size())) + { + buffer_[i] = static_cast(xs[i]); + } + return make_span(buffer_); + } + } + + private: + std::vector buffer_; +}; + +//---------------------------------------------------------------------------// +/*! + * Perform consistency checks on grids built in \c Collections. + */ +class GridValidator +{ + public: + //!@{ + //! \name Type aliases + using Grid = GenericGridRecord; + using GridId = OpaqueId; + using ImportPhysicsTable = std::vector; + + template + using Items = Collection; + //!@} + + //! Whether to use soft or exact equivalence + enum Softness + { + Soft, + Exact + }; + + public: + // Construct validator for underlying storage + GridValidator(Items* reals, Items* grids); + + // Check the imported data is built under the given grid ID range + void check_built_table(ImportPhysicsTable const& table, + ItemRange grid_ids, + Softness soft); + + // Check the imported data is built under the given grid ID + void check_built_grid(ImportPhysicsVector const& expected, + GridId grid_id, + Softness soft); + + //! Check the imported data is built in the given data range + template + void check_built_vector(T const& t, + ItemRange const& real_ids, + Softness soft) + { + this->check_span(convert_real_(t), real_ids, soft); + } + + // Construct an MFP builder with the underlying collections + MfpBuilder create_mfp_builder(); + + private: + Items* reals_; + Items* grids_; + RealSpanGenerator convert_real_; + + // Check the imported data is built in the given data range + void check_span(Span const& t, + ItemRange const& real_ids, + Softness soft); +}; + +//---------------------------------------------------------------------------// +/*! + * A \c GridValidator that stores its own collections. + */ +class GridStorage : public GridValidator +{ + public: + // Construct with internal collections + GridStorage(); + + private: + Items reals_; + Items grids_; +}; + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace optical +} // namespace celeritas